home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #2 / Monster Media No. 2 (Monster Media)(1994).ISO / utils1 / 2m21src.zip / 2MX.ASM < prev    next >
Assembly Source File  |  1994-05-31  |  129KB  |  3,059 lines

  1.  
  2. ;┌───────────────────────────────────────────────────────────────────┐
  3. ;│                                                                   │
  4. ;│                 █████ █   █ █   █                                 │
  5. ;│                     █ ██ ██  █ █    ▄▄▄▄▄     ▄                   │
  6. ;│                 █████ █ █ █   █         █   ▄▀█                   │
  7. ;│                 █     █   █  █ █    █▀▀▀▀     █                   │
  8. ;│                 █████ █   █ █   █   █▄▄▄▄ ▄ ▄▄█▄▄                 │
  9. ;│                                                                   │
  10. ;│         2MX 2.1  -  (C) Mayo 1994  Ciriaco García de Celis.       │
  11. ;│                                                                   │
  12. ;│      SOPORTE PARA DISQUETES CON MAYOR CAPACIDAD DE LA NORMAL      │
  13. ;│                    CREADOS POR LA UTILIDAD 2MF                    │
  14. ;│                                                                   │
  15. ;│ - Sólo para XT con controladora y unidades de alta.               │
  16. ;│ - Programación directa del controlador de disquetes y del DMA.    │
  17. ;│ - Soporte para sectores de más de 512 bytes (economizar GAPs).    │
  18. ;│ - Mezcla de sectores de distinto tamaño en la misma pista.        │
  19. ;│ - Ruptura del límite habitual de 80 pistas (incluido 360K).       │
  20. ;│ - Emulación de sectores de 512 bytes en INT 13h.                  │
  21. ;│ - Función 5 (INT 13h) reforzada para formatear los nuevos discos. │
  22. ;│ - El soporte residente opera eficazmente bajo DOS y WINDOWS 3.0   │
  23. ;│                                                                   │
  24. ;│      Emplear TASM /m5 y TLINK /t para obtener un fichero COM      │
  25. ;│                                                                   │
  26. ;└───────────────────────────────────────────────────────────────────┘
  27.  
  28. ; ------------ Macros de propósito general.
  29.  
  30. XPUSH          MACRO regmem            ; apilar lista de registros
  31.                  IRP rm, <regmem>
  32.                    PUSH rm
  33.                  ENDM
  34.                ENDM
  35.  
  36. XPOP           MACRO regmem            ; desapilar lista de registros
  37.                  IRP rm, <regmem>
  38.                    POP rm
  39.                  ENDM
  40.                ENDM
  41.  
  42. XPUSHA         MACRO
  43.                  XPUSH <AX, BX, CX, DX, SI, DI>
  44.                ENDM
  45.  
  46. XPOPA          MACRO
  47.                  XPOP  <DI, SI, DX, CX, BX, AX>
  48.                ENDM
  49.  
  50. XSHL           MACRO regmem, cuenta
  51.                  REPT cuenta
  52.                    SHL regmem,1
  53.                  ENDM
  54.                ENDM
  55.  
  56. DELAY          MACRO                   ; sin estados de espera
  57.                ENDM
  58.  
  59. ; ------------ Estructura de datos con información para cada unidad.
  60.  
  61. info_drv       STRUC
  62. maxs           EQU   13           ; máximo 13 sectores físicos/pista
  63. tipo_drv       DB    ?            ; tipo de la disquetera (0 = no hay)
  64. control2m_flag DB    OFF          ; a ON si 2M controla la unidad
  65. cambio         DB    ON           ; a ON indica cambio de soporte
  66. version_fmt    DB    ?            ; versión del formato de disco 2M
  67. multi_io       DB    ?            ; a 0 si posible acceso multi-sector
  68. chk            DB    ?            ; a 0 si checksum del sector 0 Ok
  69. vunidad        EQU   THIS WORD
  70. vunidad0       DB    ?            ; velocidad pista 0
  71. vunidadx       DB    ?            ; velocidad demás pistas
  72. gap            DB    ?            ; GAP entre sectores (leer/escribir)
  73. sectpista      DB    ?            ; sectores lógicos por pista
  74. tabla_tsect    DB    maxs DUP (?) ; tamaños de sectores 1, 2, ..., N
  75. tam_fat        DB    ?            ; sectores/FAT en la unidad
  76.                ENDS
  77.  
  78. ; ------------ Programa.
  79.  
  80. _PRINCIPAL     SEGMENT
  81.                ASSUME CS:_PRINCIPAL, DS:_PRINCIPAL
  82.  
  83.                ORG   100h
  84.  
  85. ini_residente  EQU   $
  86.  
  87. ; ****************************************
  88. ; *                                      *
  89. ; *   D A T O S    R E S I D E N T E S   *
  90. ; *                                      *
  91. ; ****************************************
  92.  
  93. inicio:        JMP   main
  94.  
  95. ; ------------ Identificación estandarizada del programa.
  96.  
  97. program_id     LABEL BYTE
  98. segmento_real  DW    0   ; segmento real donde será cargado
  99. offset_real    DW    0   ; offset real     "     "     "
  100. longitud_total DW    0   ; zona de memoria ocupada (párrafos)
  101. info_extra     DB    80h ; bits 0, 1 y 2-> 000: normal, con PSP
  102.                          ;                 001: bloque UMB XMS
  103.                          ;                 010: *.SYS
  104.                          ;                 011: *.SYS formato EXE
  105.                          ; bit 7 a 1: «extension_id» definida
  106. multiplex_id   DB    0   ; número Multiplex de este TSR
  107. vectores_id    DW    tabla_vectores
  108. extension_id   DW    tabla_extra
  109.                DB    "*##*"
  110. autor_nom_ver  DB    "CiriSOFT:2MX:2.1",0
  111.  
  112.                DB    2  ; número de vectores de interrupción usados
  113. tabla_vectores EQU   $
  114. vieja_i13      DB    13h           ; INT 13h
  115. ant_int13      LABEL DWORD         ; dirección original
  116. ant_int13_off  DW    0
  117. ant_int13_seg  DW    0
  118.                DB    2Fh           ; INT 2Fh
  119. ant_int2F      LABEL DWORD         ; dirección original
  120. ant_int2F_off  DW    0
  121. ant_int2F_seg  DW    0
  122.  
  123. tabla_extra    LABEL BYTE
  124.                DW    ctrl_exterior ; permitido control exterior
  125.                DW    0             ; campo reservado
  126.  
  127. ctrl_exterior  LABEL BYTE
  128. reubicabilidad DB    1             ; programa 100% reubicable
  129. activacion     DW    act
  130. act            DB    1
  131.  
  132. ; ------------ Variables del programa.
  133.  
  134. info_ptr       DW    info_A       ; punteros a datos de las unidades
  135.                DW    info_B
  136. id_sistema     DB    "2M-STV"     ; identificación de disco 2M
  137. tbase          DW    ?            ; base de tiempos para retardos
  138. unidad         DB    ?            ; unidad física de disco en curso
  139. numsect        DW    ?            ; sectores a transferir
  140. sectini        DW    ?            ; primer sector DOS a transferir
  141. cilindro       DB    ?            ; cilindro del disco a acceder
  142. cabezal        DB    ?            ; cabezal a emplear
  143. sector         DB    ?            ; número de sector físico
  144. sector_ini     DB    ?            ; número de sector físico inicial
  145. sector_fin     DB    ?            ; número de sector físico final
  146. seccion        DB    ?            ; parte del sector físico en curso
  147. secciones      DB    ?            ; sectores lógicos a transferir
  148. tsector        DB    ?            ; LOG2 (tamaño de sector) - 7
  149. buffer         DW    buffer_io    ; puntero al buffer intermedio
  150. buf_unidad     DB    ?            ; unidad del sector en el buffer
  151. buf_cilcab     DW    ?            ; cilindro/cabezal de sector buffer
  152. buf_sector     DB    ?            ; número de sector en el buffer
  153. status         DB    ?            ; resultado de los accesos a disco
  154. fdc_result     DB    7 DUP (?)    ; bytes de resultados del FDC
  155. orden          DB    ?            ; operación F_READ/F_WRITE/F_VERIFY
  156. tab_ordenes    DB    F_READ
  157.                DB    F_WRITE
  158.                DB    F_VERIFY     ; órdenes 2, 3 y 4
  159.  
  160.                ; --- Interpretación BIOS de los bits de ST1
  161.  
  162. lista_errs     DB    4            ; 'sector not found'
  163.                DB    0
  164.                DB    10h          ; 'bad CRC'
  165.                DB    8            ; 'DMA overrun'
  166.                DB    0
  167.                DB    4            ; 'sector not found'
  168.                DB    3            ; 'write-protect error'
  169.                DB    2            ; 'address mark not found'
  170.                DB    20h          ; en otro caso: 'bad NEC'
  171.  
  172. info_A         info_drv <>        ; datos de A:
  173. info_B         info_drv <>        ; datos de B:
  174.  
  175. ; ***************************************
  176. ; *                                     *
  177. ; *   C O D I G O   R E S I D E N T E   *
  178. ; *                                     *
  179. ; ***************************************
  180.  
  181. ; ------------ Rutina de gestión de INT 2Fh.
  182.  
  183. ges_int2F      PROC  FAR
  184.                STI
  185.                CMP   AH,CS:multiplex_id
  186.                JE    preguntan
  187.                JMP   CS:ant_int2F      ; saltar al gestor de INT 2Fh
  188. preguntan:     CMP   DI,1992h
  189.                JNE   ret_no_info       ; no llama alguien del convenio
  190.                MOV   AX,ES
  191.                CMP   AX,1492h
  192.                JNE   ret_no_info       ; no llama alguien del convenio
  193.                PUSH  CS
  194.                POP   ES                ; sí llama: darle información
  195.                LEA   DI,autor_nom_ver
  196. ret_no_info:   MOV   AX,0FFFFh         ; "entrada multiplex en uso"
  197.                IRET
  198. ges_int2F      ENDP
  199.  
  200. ; ------------ Nueva rutina de gestión de INT 13h. Llama a la INT 13h
  201. ;              original o a una nueva rutina de control para la
  202. ;              lectura (AH=2), escritura (AH=3) y verificación (AH=4)
  203. ;              según el tipo de disco introducido. Ante una función de
  204. ;              formateo (AH=5) se entrega el control a la INT 13h
  205. ;              original. Se detecta un posible cambio de disco y se
  206. ;              retorna en ese caso con el correspondiente error.
  207.  
  208. ges_int13      PROC  FAR
  209.                STI
  210.                CLD
  211.                PUSHF
  212.                CMP   DL,2
  213.                JAE   ges13bios         ; no es disquetera A: ó B:
  214.                PUSH  SI
  215.                CALL  set_SI_drv
  216.                CMP   CS:[SI].tipo_drv,2  ; ¿unidad 1.2M?
  217.                JE    ges_2m
  218.                CMP   CS:[SI].tipo_drv,4  ; ¿unidad 1.44/2.88M?
  219. ges_2m:        POP   SI
  220.                JC    ges13bios         ; no es unidad de alta densidad
  221.                CMP   AH,2
  222.                JB    ges13bios         ; no Read/Write/Verify/Format
  223.                CMP   AH,5
  224.                JA    ges13bios         ; no Read/Write/Verify/Format
  225.                CALL  detecta_cambio    ; ¿cambio de disco?
  226.                JNC   sin_cambio
  227.                POPF
  228.                STC                     ; hubo cambio:
  229.                MOV   AX,600h
  230.                RET   2                 ; retornar con error
  231. sin_cambio:    CMP   AH,5
  232.                JNE   dilucida          ; no es orden de formateo
  233.                CALL  leer_lin_camb
  234.                JNZ   format_bios       ; no hay disquete en la unidad
  235.                CMP   AL,7Fh
  236.                JNE   format_bios       ; no es orden formateo de 2M
  237.                CMP   SI,"2M"
  238.                JE    format_2m         ; es orden de formateo de 2M
  239. format_bios:   CLC
  240.                CALL  set_flag_STV      ; CF = 0 -> indicar no 2M
  241. dilucida:      PUSH  SI
  242.                CALL  set_SI_drv        ; SI -> variables de la unidad
  243.                CMP   CS:[SI].control2m_flag,OFF
  244.                POP   SI
  245.                JE    ges13bios         ; la unidad la controla la BIOS
  246.                POPF
  247.                CALL  control2m         ; la controla 2M
  248.                RET   2
  249. ges13bios:     POPF
  250.                JMP   CS:ant_int13      ; saltar al gestor de INT 13h
  251.  
  252.                ; --- Función de formateo implementada por 2M. En los
  253.                ;     disquetes creados con /M todas las pistas salvo
  254.                ;     la 0 deberían ser formateadas invocando INT 13h
  255.                ;     de manera directa (con CALL) para evitar que se
  256.                ;     ejecute cierto código de WINDOWS en el modo
  257.                ;     protegido que provoca errores al formatear. Antes
  258.                ;     de formatear la primera pista física del disco se
  259.                ;     invoca la función de formateo de la INT 13h
  260.                ;     original (con un error para provocar un rápido
  261.                ;     retorno) con objeto de informar al DOS y a todos
  262.                ;     los TSR previos del cambio de soporte.
  263.  
  264. format_2m:     POPF
  265.                PUSH  DS                ; *
  266.                XPUSHA                  ; **
  267.                PUSH  CS
  268.                POP   DS
  269.                MOV   unidad,DL
  270.                CALL  set_SI_drv
  271.                MOV   cilindro,CH
  272.                MOV   cabezal,DH
  273.                OR    CH,DH
  274.                JNZ   format_trx        ; no es cilindro 0 y cabezal 0
  275.                XPUSHA
  276.                MOV   AL,1              ; formatear un sector (AH=5)
  277.                MOV   CH,0
  278.                MOV   DH,2              ; en cabezal 2 (incorrecto)
  279.                PUSHF
  280.                CALL  ant_int13         ; avisar al DOS del nuevo disco
  281.                CLD                     ; mantener DF=0
  282.                STC
  283.                CALL  reset_drv         ; asegurar aceleración motor
  284.                XPOPA
  285.                CALL  set_info          ; características nuevo soporte
  286. format_trx:    CALL  genera_info       ; tabla de información formateo
  287.                CALL  motor_ok          ; asegurar que está en marcha
  288.                CALL  seek_drv
  289.                CALL  formatea_pista
  290.                PUSHF
  291.                CLC
  292.                CALL  motor_off_cnt     ; cuenta normal detención motor
  293.                POPF
  294.                CALL  set_err
  295.                CALL  set_bios_err      ; no altera flags
  296.                XPOPA                   ; **
  297.                MOV   AH,status
  298.                POP   DS                ; *
  299.                RET   2
  300. ges_int13      ENDP
  301.  
  302. ; ------------ A la entrada en DL se indica la unidad y a la salida se
  303. ;              devuelve SI apuntando sus variables sin alterar flags.
  304.  
  305. set_SI_drv     PROC
  306.                PUSHF
  307.                PUSH  BX
  308.                MOV   BL,DL
  309.                MOV   BH,0
  310.                SHL   BX,1
  311.                MOV   SI,CS:[BX+OFFSET info_ptr]
  312.                POP   BX
  313.                POPF
  314.                RET
  315. set_SI_drv     ENDP
  316.  
  317. ; ------------ Si CF=1, indicar disquete 2M presente. A la
  318. ;              entrada, DL indica la unidad de disco.
  319.  
  320. set_flag_STV   PROC
  321.                XPUSHA
  322.                CALL  set_SI_drv
  323.                MOV   AL,ON                       ; indicar 2M
  324.                JC    tipo_stv_ok
  325.                MOV   AL,OFF                      ; indicar no 2M
  326. tipo_stv_ok:   MOV   CS:[SI].control2m_flag,AL
  327.                XPOPA
  328.                RET
  329. set_flag_STV   ENDP
  330.  
  331. ; ------------ Devolver ZF=1 si cilindro y cabezal 0.
  332.  
  333. pista0?        PROC
  334.                PUSH  AX
  335.                MOV   AL,cabezal
  336.                OR    AL,cilindro
  337.                POP   AX
  338.                RET
  339. pista0?        ENDP
  340.  
  341. ; ------------ Devolver ZF=1 si la línea de cambio de disco está
  342. ;              inactiva. A la entrada, DL contiene la unidad. El
  343. ;              motor es puesto en marcha y, si no lo estaba ya, la
  344. ;              variable que indica lo que resta para detenerlo
  345. ;              es llevada a su valor normal, por lo que el disco no
  346. ;              tardará mucho en detenerse (incluso sin quizá haber
  347. ;              acelerado aún). En la práctica, invocando esta rutina
  348. ;              desde INT 13h nunca será necesario arrancar el motor
  349. ;              ya que el DOS ejecuta antes la función equivalente,
  350. ;              la 16h, que lo pone en marcha. Es simplemente una
  351. ;              medida de seguridad contra las BIOS «de marca».
  352.  
  353. leer_lin_camb  PROC
  354.                XPUSHA                  ; *
  355.                PUSH  DS
  356.                MOV   CX,40h
  357.                MOV   DS,CX
  358.                MOV   AL,1
  359.                MOV   CL,DL
  360.                SHL   AL,CL             ; bit de motor en 0..3
  361.                TEST  DS:[3Fh],AL
  362.                JNZ   rodando           ; el motor ya está girando
  363.                CLC
  364.                CALL  motor_off_cnt     ; cuenta normal detención motor
  365. rodando:       MOV   AH,DL
  366.                XSHL  AH,4
  367.                OR    AH,AL             ; AH = byte BIOS
  368.                XSHL  AL,4
  369.                OR    AL,00001100b      ; modo DMA, no hacer reset
  370.                OR    AL,DL             ; AL para reg. salida digital
  371.                MOV   DX,3F2h
  372.                CLI
  373.                MOV   DS:[3Fh],AH       ; actualizar variable BIOS
  374.                OUT   DX,AL             ; arrancado motor en la unidad
  375.                ADD   DX,5
  376.                DELAY
  377.                IN    AL,DX             ; leer línea de cambio de disco
  378.                STI
  379.                TEST  AL,80h            ; ZF=0 -> cambio de disco
  380.                POP   DS
  381.                XPOPA                   ; *
  382.                RET
  383. leer_lin_camb  ENDP
  384.  
  385. ; ------------ Determinar si ha habido cambio de disco y, en ese caso,
  386. ;              si el nuevo disquete es de tipo 2M o no. El cambio de
  387. ;              disco se detecta leyendo la línea de cambio de disco o
  388. ;              chequeando la variable que indica si ha habido cambio
  389. ;              o no (esta variable está a ON tras instalar 2M para
  390. ;              forzar la detección del tipo de disco introducido; se
  391. ;              pone en ON también si no se logra bajar la línea de
  392. ;              cambio de disco por si fuera un soporte raro y la BIOS
  393. ;              sí lo lograra -forzando así una detección posterior-).
  394.  
  395. detecta_cambio PROC
  396.                XPUSHA                  ; *
  397.                CALL  set_SI_drv        ; SI -> variables de la unidad
  398.                CMP   CS:[SI].cambio,ON ; ¿cambio de soporte?
  399.                MOV   CS:[SI].cambio,OFF
  400.                JE    hubo_cambio
  401.                CALL  leer_lin_camb     ; leer línea de cambio de disco
  402.                JNZ   hubo_cambio
  403.                XPOPA
  404.                CLC                     ; no hay cambio de disco
  405.                RET
  406. hubo_cambio:   CLC
  407.                CALL  set_flag_STV      ; CF = 0 -> supuesto no 2M
  408.                XPUSH <DS, ES>          ; **
  409.                MOV   BX,90h
  410.                ADD   BL,DL
  411.                MOV   CX,40h
  412.                MOV   DS,CX
  413.                AND   BYTE PTR [BX],255-16 ; densidad no determinada
  414.                XPUSH <CS, CS>
  415.                XPOP  <DS, ES>
  416.                MOV   unidad,DL
  417.                STC                     ; asegurar motor en marcha
  418.                CALL  reset_drv
  419.                MOV   cilindro,1
  420.                MOV   cabezal,0
  421.                CALL  seek_drv          ; bajar línea cambio de disco
  422.                DEC   cilindro
  423.                CALL  seek_drv
  424.                CLC
  425.                CALL  motor_off_cnt     ; cuenta normal detención motor
  426.                CALL  leer_lin_camb     ; ¿bajada línea cambio disco?
  427.                JZ    disco_dentro      ; se pudo: hay disco dentro
  428.                MOV   [SI].cambio,ON    ; futura detección tipo disco
  429.                CLC                     ; NO indicar cambio de disco...
  430.                JMP   fin_detecta       ; ...para pasar control a BIOS
  431. disco_dentro:  PUSH  DS
  432.                MOV   CX,40h
  433.                MOV   DS,CX
  434.                MOV   BYTE PTR DS:[41h],6  ; error 'media changed'
  435.                POP   DS
  436.                CMP   AH,5              ; ¿función de formateo?
  437.                JE    fin_detecta_c     ; no perder el tiempo
  438.                MOV   buf_unidad,-1     ; invalidar buffer
  439.                MOV   [SI].gap,20       ; GAP provisional
  440.                MOV   CX,3              ; 3 intentos
  441. intenta_io0:   PUSH  CX
  442.                CMP   CX,2              ; CF=1 la 3ª vez (a 0 si CX<>1)
  443.                CALL  reset_drv
  444.                MOV   [SI].vunidad0,0   ; empezar con 500 Kbit/seg.
  445. intenta_io:    MOV   cilindro,0
  446.                MOV   cabezal,0
  447.                MOV   sector,1          ; sector de arranque
  448.                MOV   seccion,0
  449.                MOV   secciones,1
  450.                MOV   orden,F_READ
  451.                MOV   DI,buffer
  452.                CALL  direct_acceso
  453.                JNE   otra_densidad     ; es otra densidad de disco
  454.                POP   CX
  455.                MOV   BX,buffer
  456.                CALL  set_info          ; características nuevo soporte
  457.                CLC
  458.                JMP   fin_detecta_c     ; indicar cambio de disco
  459. otra_densidad: MOV   AL,[SI].vunidad0
  460.                INC   AX                ; próxima velocidad
  461.                CMP   AL,3
  462.                JA    otro_intento
  463.                MOV   [SI].vunidad0,AL
  464.                JMP   intenta_io        ; probar otra velocidad
  465. otro_intento:  MOV   [SI].vunidad0,0
  466.                POP   CX
  467.                LOOP  intenta_io0       ; reintento
  468. fin_detecta_c: STC                     ; indicar cambio de disco
  469. fin_detecta:   XPOP  <ES, DS>          ; **
  470.                XPOPA                   ; *
  471.                RET
  472. detecta_cambio ENDP
  473.  
  474. ; ------------ Anotar la información del disquete si es de tipo 2M.
  475. ;              A la entrada, DS:SI apunta a las variables de la unidad
  476. ;              y ES:BX al sector de arranque del disco. Se actualiza
  477. ;              también la variable BIOS de tipo de densidad (la BIOS
  478. ;              no se da cuenta del cambio de disco y conviene ayudar).
  479.  
  480. set_info       PROC
  481.                XPUSHA
  482.                CALL  calc_chk
  483.                JC    set_info_exit     ; no es disco 2M
  484.                MOV   [SI].chk,AL         ; anotar checksum
  485.                MOV   [SI].version_fmt,CL ; y versión del formato
  486.                MOV   DL,unidad
  487.                STC
  488.                CALL  set_flag_STV      ; CF = 1 -> indicar disco 2M
  489.                MOV   AL,ES:[BX+22]     ; tamaño de FAT
  490.                MOV   [SI].tam_fat,AL
  491.                MOV   CL,ES:[BX+65]     ; CL a 0 si acceso multi-sector
  492.                MOV   [SI].multi_io,CL
  493.                MOV   AX,ES:[BX+66]
  494.                MOV   [SI].vunidad,AX   ; velocidad pista 0 / demás
  495.                MOV   AL,ES:[BX+24]
  496.                MOV   [SI].sectpista,AL ; sectores/pista
  497.                MOV   DI,ES:[BX+72]
  498.                MOV   AL,ES:[BX+DI+1]   ; GAP de formateo
  499.                MOV   AH,AL
  500.                AND   CL,CL             ; CL a 0 si acceso multi-sector
  501.                JZ    gap_rw_ok         ; GAP R/W para /F
  502.                ADD   AH,190
  503.                MOV   AL,11
  504.                MUL   AH                ; AX = (190+GAP)*11
  505.                SUB   AX,2048+62
  506. gap_rw_ok:     SHR   AL,1              ; GAP R/W para /M
  507.                MOV   [SI].gap,AL
  508.                MOV   CX,maxs
  509.                MOV   DI,ES:[BX+74]
  510.                ADD   DI,BX
  511.                LEA   BX,[SI].tabla_tsect
  512. genera_ts:     MOV   AL,ES:[DI]
  513.                MOV   [BX],AL
  514.                INC   BX
  515.                INC   DI
  516.                LOOP  genera_ts         ; información estructura pistas
  517. set_info_exit: MOV   AL,[SI].vunidad0
  518.                MOV   CL,6
  519.                SHL   AL,CL             ; velocidad en bits 7:6
  520.                OR    AL,00010111b      ; establecido otro medio físico
  521.                CMP   [SI].tipo_drv,2
  522.                JA    modo_ok           ; es unidad de 3½
  523.                AND   AL,11111000b
  524.                OR    AL,00000101b      ; 1.2 en 1.2
  525.                TEST  AL,01000000b
  526.                JZ    modo_ok
  527.                XOR   AL,00100001b      ; 360 en 1.2 y seek * 2
  528. modo_ok:       PUSH  DS
  529.                MOV   BX,90h
  530.                ADD   BL,unidad
  531.                PUSH  40h
  532.                POP   DS
  533.                AND   BYTE PTR DS:[BX],8  ; respetar bit de 2.88M
  534.                OR    DS:[BX],AL        ; actualizar variable BIOS
  535.                POP   DS                ; con el tipo de densidad
  536.                XPOPA
  537.                RET
  538. set_info       ENDP
  539.  
  540. ; ------------ Calcular el checksum de la zona vital del sector de
  541. ;              arranque. A la entrada, ES:BX -> sector de arranque.
  542. ;              A la salida, CF=1 si el disco no es 2M; de otro modo
  543. ;              checksum en AL y versión del formato de disco en CL.
  544.  
  545. calc_chk       PROC
  546.                XPUSH <SI, DI>
  547.                LEA   DI,[BX+3]         ; DI=BX+3
  548.                LEA   SI,id_sistema
  549.                MOV   CX,6
  550.                REP   CMPSB             ; comparar identificación
  551.                STC
  552.                JNE   chk_ret           ; el disco no es 2M
  553.                XOR   AX,AX
  554.                MOV   CL,ES:[BX+64]     ; versión del formateador
  555.                CMP   CL,6
  556.                JB    chk_ok            ; no usaba este checksum
  557.                MOV   DI,ES:[BX+68]
  558. chk_sum:       DEC   DI
  559.                ADD   AL,ES:[BX+DI]
  560.                CMP   DI,63
  561.                JA    chk_sum
  562. chk_ok:        CLC
  563. chk_ret:       XPOP  <DI, SI>
  564.                RET
  565. calc_chk       ENDP
  566.  
  567. ; ------------ Determinar el tipo de error producido en el acceso.
  568.  
  569. set_err        PROC
  570.                XPUSH <AX, BX, CX>
  571.                JNC   err_ret           ; no hay error
  572.                CMP   status,0          ; ¿'status' ya asignado?
  573.                JNE   err_retc          ; no cambiarlo si es así
  574.                MOV   AL,BYTE PTR fdc_result+1
  575.                AND   AL,10110111b      ; aislar condiciones de test
  576.                LEA   BX,lista_errs
  577.                MOV   CX,9
  578. busca_err:     MOV   AH,[BX]           ; código de error BIOS
  579.                SHL   AL,1
  580.                JC    err_ok            ; es ese error
  581.                INC   BX
  582.                LOOP  busca_err         ; buscar otro error
  583. err_ok:        OR    status,AH
  584. err_retc:      STC                     ; condición de error
  585. err_ret:       XPOP  <CX, BX, AX>
  586.                RET
  587. set_err        ENDP
  588.  
  589. ; ------------ Actualizar variables de error de la BIOS.
  590.  
  591. set_bios_err   PROC
  592.                PUSHF                   ; *
  593.                XPUSHA                  ; **
  594.                PUSH  ES                ; ***
  595.                MOV   CX,40h
  596.                MOV   ES,CX
  597.                MOV   DI,41h            ; bytes de resultados del 765
  598.                LEA   SI,status         ; variable BIOS de status y 7
  599.                MOV   CX,4              ; bytes: 4 palabras
  600.                REP   MOVSW
  601.                POP   ES                ; ***
  602.                XPOPA                   ; **
  603.                POPF                    ; *
  604.                RET
  605. set_bios_err   ENDP
  606.  
  607. ; ------------ Realizar lecturas, escrituras y verificaciones: rutina
  608. ;              que sustituye el código de la BIOS para poder soportar
  609. ;              los formatos 2M. La operación puede quedar dividida en
  610. ;              tres fases: el fragmento anterior a la FAT2, la zona
  611. ;              correspondiente a la FAT2 (se ignora la escritura y se
  612. ;              simula su lectura leyendo la FAT1) y un último bloque
  613. ;              ubicado tras la FAT2. El sector de arranque es emulado
  614. ;              empleando el primer sector físico de la FAT2 (aunque en
  615. ;              los discos de versión de formato anterior a la 7 se usa
  616. ;              el sector de arranque verdadero -permitiendo escribirlo
  617. ;              sólo si es válido-). En cualquier caso, si el número de
  618. ;              cabezal tiene el bit 7 activo, se sobreentiende que el
  619. ;              programa que llama soporta disquetes 2M y no se emula
  620. ;              la FAT2 ni el sector de arranque, para permitirle
  621. ;              acceder al código SuperBOOT. Las coordenadas de la BIOS
  622. ;              se traducen a las unidades del DOS por mayor comodidad.
  623.  
  624. control2m      PROC
  625.                PUSH  DS                ; *
  626.                XPUSHA                  ; **
  627.                PUSH  CS
  628.                POP   DS
  629.                MOV   unidad,DL
  630.                CALL  set_SI_drv        ; SI -> variables de la unidad
  631.                CMP   [SI].chk,0
  632.                JE    chk_valido        ; checksum correcto en sector 0
  633.                MOV   status,40h        ; devolver 'Seek Error' al DOS
  634.                JMP   exit_2m_ctrl
  635. chk_valido:    PUSH  AX                ; ***
  636.                MOV   AH,0
  637.                MOV   numsect,AX        ; nº sectores
  638.                MOV   AL,CH             ; cilindro
  639.                SHL   AL,1
  640.                MOV   DL,DH
  641.                AND   DH,01111111b
  642.                ADD   AL,DH             ; cabezal físico
  643.                MUL   [SI].sectpista
  644.                ADD   AL,CL             ; sector
  645.                ADC   AH,0
  646.                DEC   AX                ; AX = nº sector DOS
  647.                MOV   sectini,AX
  648.                MOV   DI,BX             ; ES:DI -> dirección
  649.                POP   BX                ; ***
  650.                MOV   BL,BH
  651.                MOV   BH,0
  652.                MOV   CL,[BX+OFFSET tab_ordenes-2]
  653.                MOV   orden,CL
  654.                SHL   DL,1
  655.                JC    acceso_final      ; cabezal >= 128: no emular
  656.                AND   AX,AX             ; ¿comienza en sector 0?
  657.                JNZ   io_emula          ; no
  658.                CMP   [SI].version_fmt,7
  659.                JB    boot_real         ; no soportado BOOT virtual
  660.                MOV   AL,[SI].tam_fat   ; AH = 0
  661.                INC   AX
  662.                MOV   CX,1              ; sector BOOT emulado en
  663.                CALL  ejecuta_io        ; el primer sector FAT2
  664.                JNE   fin_ctrl
  665. boot_fin_op:   DEC   numsect
  666.                INC   sectini
  667.                MOV   AX,sectini
  668.                JMP   io_emula
  669. boot_real:     CMP   orden,F_WRITE
  670.                JNE   io_emula
  671.                MOV   BX,DI             ; BOOT de 2M 1.3 y anteriores
  672.                CALL  calc_chk
  673.                JC    si_skip           ; no es de tipo 2M
  674.                AND   AL,AL
  675.                JZ    io_emula          ; lo es y con checksum correcto
  676. si_skip:       ADD   DI,512
  677.                JMP   boot_fin_op       ; impedir estropicio de BOOT
  678. io_emula:      MOV   CL,[SI].tam_fat
  679.                MOV   CH,0              ; CX = primer sector FAT2 - 1
  680.                CMP   AX,CX
  681.                JA    en_fat2?          ; ¿la operación afecta a FAT2?
  682.                CALL  calc_iop          ; calcular sectores antes FAT2
  683.                CALL  ejecuta_io        ; CX sectores desde AX
  684.                JNE   fin_ctrl          ; error
  685.                CMP   numsect,0
  686.                JE    fin_ctrl          ; fin de la transferencia
  687. en_fat2?:      MOV   AX,sectini
  688.                MOV   CL,[SI].tam_fat
  689.                MOV   CH,0
  690.                SHL   CX,1              ; CX = último sector FAT2
  691.                CMP   AX,CX
  692.                JA    acceso_final      ; la operación es tras la FAT2
  693.                CALL  calc_iop          ; sectores hasta fin de FAT2
  694.                CMP   orden,F_WRITE
  695.                JNE   emula_fat1
  696.                XCHG  CH,CL
  697.                SHL   CH,1              ; CX = CX * 512
  698.                ADD   DI,CX             ; ES:DI actualizado
  699.                JMP   acceso_final
  700. emula_fat1:    MOV   DL,[SI].tam_fat
  701.                MOV   DH,0
  702.                SUB   AX,DX             ; leer de FAT1 y no de la FAT2
  703.                CALL  ejecuta_io        ; CX sectores desde AX
  704.                JNE   fin_ctrl          ; error
  705. acceso_final:  CMP   numsect,0
  706.                JE    fin_ctrl          ; fin de la transferencia
  707.                MOV   AX,sectini
  708.                MOV   CX,numsect
  709.                CALL  ejecuta_io
  710. fin_ctrl:      CLC
  711.                CALL  motor_off_cnt     ; cuenta normal detención motor
  712.                CALL  set_bios_err      ; actualizar variables BIOS
  713. exit_2m_ctrl:  XPOPA                   ; **
  714.                MOV   AH,status
  715.                POP   DS                ; *
  716.                AND   AH,AH
  717.                JZ    st_ok             ; resultado correcto (CF=0)
  718.                STC                     ; error
  719.                MOV   AL,0              ; 0 sectores movidos
  720. st_ok:         RET
  721. calc_iop:      SUB   CX,AX
  722.                INC   CX                ; CX sectores
  723.                CMP   CX,numsect
  724.                JBE   nsect_ok
  725.                MOV   CX,numsect        ; sólo quedan CX
  726. nsect_ok:      SUB   numsect,CX
  727.                ADD   sectini,CX
  728.                RET
  729. control2m      ENDP
  730.  
  731. ; ------------ A la entrada, AX indica el sector inicial (coordenadas
  732. ;              del DOS) y CX el número de sectores a procesar.
  733. ;              * Definiciones: «Sector físico» es un sector del disco
  734. ;              de 512, 1024 ó 2048 bytes (números de sector del 1 al N
  735. ;              en la pista). Este sector físico está dividido en
  736. ;              «secciones» de 512 bytes, constando por tanto de 1, 2 ó
  737. ;              4 secciones. «Sector virtual» es el número de sector
  738. ;              del programa que llama a INT 13h, comprendido entre 1 y
  739. ;              M. Esta estructura de N sectores por pista de distintos
  740. ;              tamaños, se verifica en todo el disco con excepción del
  741. ;              cabezal y cilindro 0 (con un formato más convencional
  742. ;              de sectores de 512 bytes numerados de 1 a J, aunque no
  743. ;              existen algunos de los intermedios que corresponden a
  744. ;              la segunda copia de la FAT).
  745. ;              * Primero se convierte el sector virtual (1..M) en su
  746. ;              correspondiente físico (1..J en la pista 0 y 1..N en
  747. ;              las demás), deduciendo qué porción de 512 bytes (o
  748. ;              sección) es afectada. Un sector virtual (512 bytes)
  749. ;              simulado suele ser parte de un sector físico de 2048
  750. ;              bytes en muchos casos. Si dicho sector físico ya había
  751. ;              sido leído al buffer en anteriores accesos, se extrae
  752. ;              la sección necesaria. Si no, se carga del disco y se
  753. ;              extrae dicho fragmento. El número de sectores virtuales
  754. ;              que se solicitan (=secciones) permite realizar un bucle
  755. ;              hasta completar la transferencia; el interleave 1:2 de
  756. ;              los sectores físicos en /M permite acceder sector a
  757. ;              sector sin pérdida de rendimiento. En el caso de la
  758. ;              escritura, se estudia primero si hay varios sectores
  759. ;              virtuales consecutivos que escribir, completando entre
  760. ;              todos un sector físico: en ese caso, se prepara el
  761. ;              mismo y se escribe sin más. En caso de que haya que
  762. ;              modificar sólo una única sección de un sector físico,
  763. ;              salvo si éste es de 512 bytes, no hay más remedio que
  764. ;              cargarlo al buffer (realizar una prelectura),
  765. ;              actualizar la sección correspondiente y volverlo a
  766. ;              escribir.
  767. ;              * En el formato /F se realiza una operación multisector
  768. ;              si es posible y sin emplear el buffer intermedio (si
  769. ;              bien podría ser preciso emplearlo con la primera y
  770. ;              última sección); en los dos formatos de disco se hace
  771. ;              la operación multisector en la primera pista. Las
  772. ;              operaciones multisector puede que sea preciso
  773. ;              dividirlas en tres fases: los sectores antes de una
  774. ;              frontera de DMA, el que la cruza (que es transferido
  775. ;              a través del buffer intermedio) y los que están detrás.
  776.  
  777. ejecuta_io     PROC
  778.                MOV   BX,AX             ; AX = sector DOS inicial
  779.                MOV   secciones,CL      ; CX sectores (CL realmente)
  780.                DIV   [SI].sectpista
  781.                INC   AH                ; numerado desde 1...
  782.                MOV   sector,AH         ; ...el resto es el sector
  783.                SHR   AL,1
  784.                MOV   cilindro,AL       ; cilindro
  785.                RCL   AL,1
  786.                AND   AL,1
  787.                MOV   cabezal,AL        ; cabezal
  788.                MOV   AL,sector
  789.                ADD   AL,secciones
  790.                JC    no_cabe           ; sector+secciones > 255
  791.                DEC   AX                ; DEC AX = DEC AL
  792.                CMP   AL,[SI].sectpista
  793.                JBE   si_cabe
  794. no_cabe:       MOV   status,4          ; 'sector no encontrado'
  795.                JMP   fin_io
  796. si_cabe:       MOV   AL,AH             ; sector en AL
  797.                CBW                     ; sección 0 (AH = 0)
  798.                CALL  pista0?
  799.                JZ    s_xx              ; sector físico en pista/cara 0
  800.                LEA   BX,[SI].tabla_tsect-1
  801.                DEC   AX                ; AH = 0
  802. resta_secc:    INC   BX
  803.                INC   AH
  804.                MOV   CL,[BX]
  805.                SUB   CL,2
  806.                MOV   CH,1
  807.                SHL   CH,CL
  808.                SUB   AL,CH
  809.                JNC   resta_secc        ; en las demás pistas
  810.                ADD   AL,CH
  811.                XCHG  AH,AL
  812. s_xx:          MOV   sector,AL         ; sector lógico convertido a
  813.                MOV   seccion,AH        ; sector y sección físicas
  814. direct_acceso: CALL  motor_ok          ; asegurar que está en marcha
  815.                MOV   AH,0
  816.                MOV   sector_fin,AH     ; no acceder a más de 1 sector
  817.                CALL  pista0?           ; (al menos de momento)
  818.                JNZ   decide_multi      ; no es pista 0
  819.                MOV   AL,secciones
  820.                MOV   secciones,AH      ; las que restan (AH = 0)
  821.                JMP   multi_proc
  822. decide_multi:  CMP   [SI].multi_io,AH  ; AH = 0
  823.                JNE   io_pasos          ; acceso sector a sector
  824.                CMP   seccion,AH
  825.                JE    multi_acc
  826.                CALL  acceso_secc       ; no acceso a inicio sector
  827.                JC    fin_io
  828. multi_acc:     CMP   secciones,AH      ; AH = 0
  829.                JE    fin_io
  830.                CALL  num_secciones
  831.                MOV   CL,AL
  832.                MOV   AL,secciones      ; AH = 0
  833.                DIV   CL
  834.                AND   AL,AL
  835.                JZ    io_pasos          ; no quedan sectores enteros
  836.                MOV   secciones,AH      ; las que restan
  837. multi_proc:    CALL  acceso_multi      ; de AL sectores
  838.                JC    fin_io
  839. io_pasos:      CMP   secciones,0
  840.                JE    fin_io            ; no restan secciones finales
  841.                CALL  acceso_secc
  842.                JNC   io_pasos
  843. fin_io:        CMP   status,0          ; ZF = 1 -> operación correcta
  844.                RET
  845.  
  846. acceso_secc:   PUSH  AX
  847.                CMP   orden,F_WRITE     ; acabar transferencia sector
  848.                JE    escritura
  849.                CMP   orden,F_VERIFY
  850.                JE    verificacion
  851.                CALL  leido?            ; realizar lectura...
  852.                JNC   ya_leido          ; sector ya en el buffer
  853. hay_que_leer:  CALL  acceso_sector     ; efectuar E/S
  854.                JC    acc_ret           ; ha habido fallo
  855. ya_leido:      CALL  trans_secc        ; buffer -> memoria
  856.                JMP   acc_ret
  857. escritura:     CMP   seccion,0
  858.                JNE   prelectura        ; sólo parte del sector cambia
  859.                CALL  num_secciones
  860.                CMP   secciones,AL
  861.                JAE   escribir          ; Todo el sector físico cambia
  862. prelectura:    CALL  leido?            ; Leer el sector físico para
  863.                JNC   escribir          ; cambiar sólo una parte de él
  864.                MOV   orden,F_READ      ; de momento leer...
  865.                CALL  acceso_sector     ; efectuar E/S
  866.                MOV   orden,F_WRITE     ; ... restaurar orden original
  867.                JC    acc_ret           ; ha habido fallo
  868. escribir:      CALL  trans_secc        ; memoria -> buffer
  869.                CALL  acceso_sector     ; volcar buffer al disco
  870.                JMP   acc_ret
  871. verificacion:  PUSH  BX
  872.                MOV   BL,seccion
  873.                CALL  num_secciones
  874. dec_sec_veri:  DEC   secciones
  875.                JZ    verifica
  876.                INC   BX
  877.                CMP   BL,AL
  878.                JB    dec_sec_veri
  879. verifica:      POP   BX
  880.                CALL  acceso_sector     ; leer para forzar verificación
  881. acc_ret:       PUSHF
  882.                INC   sector            ; preparado para otro sector
  883.                MOV   seccion,0         ; desde su primera sección
  884.                POPF
  885.                POP   AX
  886.                RET
  887.  
  888. acceso_multi:  PUSH  AX                ; AL = sectores a transferir
  889.                MOV   BX,ES             ;      desde 'sector' teniendo
  890.                XSHL  BX,4              ;      cuidado con el DMA
  891.                ADD   BX,DI
  892.                NEG   BX                ; BX = bytes hasta frontera DMA
  893.                CALL  num_secciones
  894.                MOV   CH,AL             ; AL secciones de 512 bytes
  895.                MOV   CL,0
  896.                SHL   CX,1              ; CX = bytes por sector
  897.                XCHG  AX,BX             ; BL = secciones por sector
  898.                XOR   DX,DX
  899.                DIV   CX
  900.                MOV   CL,AL             ; CL = sectores que caben
  901.                POP   AX                ; AL = sectores a transferir
  902.                CMP   AL,CL
  903.                JA    acc_mult2         ; no hay problemas con el DMA
  904. acc_mult1:     MOV   CL,AL
  905. acc_mult2:     AND   CL,CL
  906.                JZ    acc_mult3         ; primer sector problemático
  907.                MOV   AH,sector
  908.                MOV   sector_ini,AH
  909.                ADD   AH,CL
  910.                DEC   AH
  911.                MOV   sector_fin,AH
  912.                INC   AH
  913.                SUB   AL,CL
  914.                CALL  acceso_sector     ; sectores no problemáticos
  915.                MOV   sector,AH
  916.                JC    acc_mult_fin
  917. acc_mult3:     AND   AL,AL             ; ahora el sector problemático
  918.                JZ    acc_mult_fin
  919.                ADD   secciones,BL      ; compensar futuro decremento
  920.                CALL  acceso_secc       ; a través del buffer auxiliar
  921.                JC    acc_mult_fin
  922.                DEC   AL
  923.                JMP   acc_mult1         ; sectores que restan
  924. acc_mult_fin:  RET
  925. ejecuta_io     ENDP
  926.  
  927. ; ------------ Mover secciones desde el buffer hacia la memoria (con
  928. ;              orden F_READ) después de la lectura o de la memoria al
  929. ;              buffer (orden F_WRITE) antes de la escritura. En la
  930. ;              verificación (orden F_VERIFY) no se mueve nada porque
  931. ;              esta subrutina no es invocada.
  932.  
  933. trans_secc     PROC
  934.                XPUSH <AX, BX, CX, SI>  ; *
  935.                MOV   BL,seccion        ; desde esta sección
  936.                CALL  num_secciones     ; nº secciones del sector
  937. otra_secci:    PUSH  BX
  938.                MOV   BH,BL
  939.                SHL   BH,1
  940.                MOV   BL,0              ; sección * 512
  941.                ADD   BX,buffer         ; dirección
  942.                MOV   SI,BX
  943.                MOV   CX,256            ; tamaño sección (palabras)
  944.                CALL  swap_reg          ; ¿intercambiar origen-destino?
  945.                REP   MOVSW             ; copiar 512 bytes
  946.                CALL  swap_reg          ; ¿intercambiar origen-destino?
  947.                POP   BX
  948.                DEC   secciones         ; una menos
  949.                JZ    fin_secc
  950.                INC   BX                ; otra sección del sector
  951.                CMP   BL,AL             ; ¿sector agotado?
  952.                JB    otra_secci        ; aún no
  953. fin_secc:      XPOP  <SI, CX, BX, AX>  ; *
  954.                RET
  955. swap_reg:      CMP   CS:orden,F_WRITE
  956.                JE    interc
  957.                CLC
  958.                RET
  959. interc:        XCHG  SI,DI             ; en escritura, invertir el
  960.                XPUSH <ES, DS>          ; sentido de la operación
  961.                XPOP  <ES, DS>
  962.                RET
  963. trans_secc     ENDP
  964.  
  965. ; ------------ Comprobar si el sector ya está en el buffer.
  966.  
  967. leido?         PROC
  968.                PUSH  AX
  969.                MOV   AL,buf_unidad
  970.                CMP   AL,unidad
  971.                JNE   no_leido          ; es en otra unidad
  972.                MOV   AL,cilindro
  973.                MOV   AH,cabezal
  974.                CMP   AX,buf_cilcab
  975.                JNE   no_leido          ; es en otro cilindro/cabezal
  976.                MOV   AL,buf_sector
  977.                CMP   AL,sector
  978.                JNE   no_leido          ; es otro sector
  979.                POP   AX
  980.                RET                     ; está en el buffer
  981. no_leido:      STC
  982.                POP   AX
  983.                RET                     ; sector no leído
  984. leido?         ENDP
  985.  
  986. ; ------------ Leer o escribir sector(es). Se selecciona el tamaño de
  987. ;              sector correcto antes de llamar a sector_io.  En esta
  988. ;              rutina se actualiza la variable «status» en función de
  989. ;              los posibles errores de acceso.  Si sector_fin es
  990. ;              distinto de 0 se accede a los sectores indicados, si es
  991. ;              0 se accede sólo al sector «sector» a través del buffer
  992. ;              intermedio y al final se anota el sector cargado ó
  993. ;              escrito para evitar futuras lecturas innecesarias, a
  994. ;              modo de mini-caché que dispara la velocidad de acceso a
  995. ;              sectores lógicos consecutivos.
  996.  
  997. acceso_sector  PROC
  998.                XPUSH <AX, BX>
  999.                CALL  seek_drv          ; posicionar el cabezal
  1000.                JNC   en_pista
  1001.                CMP   status,0          ; ¿error ya determinado?
  1002.                JNE   acc_fin_err
  1003.                OR    status,40h        ; no: pues 'seek error'
  1004. acc_fin_err:   STC
  1005.                JMP   acceso_fin
  1006. en_pista:      CALL  pista0?
  1007.                MOV   AL,2
  1008.                JZ    tam_acc_ok        ; sectores 512 en cil./cab. 0
  1009.                LEA   BX,[SI].tabla_tsect
  1010.                ADD   BL,sector
  1011.                ADC   BH,0
  1012.                MOV   AL,[BX-1]
  1013. tam_acc_ok:    MOV   tsector,AL
  1014.                CMP   sector_fin,0      ; ¿usar buffer intermedio?
  1015.                JE    acceso_buffer
  1016.                CALL  sector_io
  1017.                MOV   sector_fin,0      ; no acceder a más de 1 sector
  1018.                PUSHF                   ; **1
  1019.                JMP   acceso_rep        ; en el futuro (por defecto)
  1020. acceso_buffer: XPUSH <ES, DI>
  1021.                PUSH  CS
  1022.                POP   ES
  1023.                MOV   DI,buffer         ; acceso con buffer auxiliar
  1024.                MOV   AL,sector         ; mismo sector inicial/final
  1025.                MOV   sector_ini,AL
  1026.                MOV   sector_fin,AL
  1027.                CALL  sector_io
  1028.                MOV   sector_fin,0
  1029.                XPOP  <DI, ES>
  1030.                PUSHF                   ; **2
  1031.                MOV   AL,-1             ; invalidar contenido buffer
  1032.                JC    acceso_anota      ; si hay error
  1033.                CMP   orden,F_VERIFY
  1034.                JE    acceso_rep        ; nada leído físicamente
  1035.                MOV   AL,unidad
  1036. acceso_anota:  MOV   buf_unidad,AL
  1037.                MOV   AL,cilindro
  1038.                MOV   AH,cabezal
  1039.                MOV   buf_cilcab,AX
  1040.                MOV   AL,sector
  1041.                MOV   buf_sector,AL     ; anotado el sector en buffer
  1042. acceso_rep:    POPF                    ; ** mucho cuidado con la pila
  1043.                CALL  set_err           ; ajustar variable «status»
  1044. acceso_fin:    XPOP  <BX, AX>
  1045.                RET
  1046. acceso_sector  ENDP
  1047.  
  1048. ; ------------ Devolver el número de secciones del sector en curso.
  1049.  
  1050. num_secciones  PROC
  1051.                CALL  pista0?
  1052.                MOV   AL,1
  1053.                JZ    num_secc_ok       ; sectores 512 en cil./cab. 0
  1054.                XPUSH <BX, CX>
  1055.                LEA   BX,[SI].tabla_tsect
  1056.                ADD   BL,sector
  1057.                ADC   BH,0
  1058.                MOV   CL,[BX-1]
  1059.                SUB   CL,2
  1060.                MOV   AL,1
  1061.                SHL   AL,CL
  1062.                XPOP  <CX, BX>
  1063. num_secc_ok:   RET                     ; resultado en AL
  1064. num_secciones  ENDP
  1065.  
  1066. ; ------------ Asegurar que el motor está en marcha.
  1067.  
  1068. motor_ok       PROC
  1069.                XPUSHA                  ; *
  1070.                PUSH  DS                ; **
  1071.                MOV   BX,40h
  1072.                PUSH  BX
  1073.                POP   DS
  1074.                MOV   CH,255-18         ; CH = 255 - 1 segundo
  1075.                CLI
  1076.                MOV   CL,CS:unidad
  1077.                MOV   AL,1
  1078.                SHL   AL,CL
  1079.                TEST  [BX-1],AL         ; ¿motor en marcha?
  1080.                JZ    arrancarlo        ; arrancarlo
  1081.                CMP   [BX],CH           ; Si encendido y acelerado...
  1082.                JBE   ok_motor          ; ...seguir
  1083. arrancarlo:    OR    [BX-1],AL         ; arrancar motor
  1084.                AND   BYTE PTR [BX-1],0CFh  ; borrar número unidad
  1085.                MOV   AL,CL
  1086.                XSHL  AL,4              ; unidad << 4
  1087.                OR    [BX-1],AL         ; nuevo número de unidad
  1088.                MOV   BYTE PTR [BX],255 ; asegurar que no se pare
  1089.                STI
  1090.                MOV   DX,3F2h           ; registro de salida digital
  1091.                ADD   CL,4
  1092.                MOV   AL,1
  1093.                SHL   AL,CL             ; colocar bit del motor
  1094.                OR    AL,CS:unidad      ; seleccionar unidad
  1095.                OR    AL,00001100b      ; modo DMA, no hacer reset
  1096.                OUT   DX,AL             ; poner en marcha el motor
  1097.                MOV   AX,90FDh
  1098.                CLC
  1099.                INT   15h               ; permitir multitarea
  1100.                JC    ok_motor          ; timeout
  1101.                MOV   AX,1000           ; 1 segundo aceleración
  1102.                CALL  retardo           ; esperar aceleración disco
  1103. ok_motor:      MOV   [BX],CH           ; cuenta máxima detención motor
  1104.                STI                     ; sin forzar futura aceleración
  1105.                POP   DS                ; **
  1106.                XPOPA                   ; *
  1107.                RET
  1108. motor_ok       ENDP
  1109.  
  1110. ; ------------ Establecer modalidad de operación del controlador
  1111. ;              y poner el motor en marcha. Si CF=1 se le da tiempo
  1112. ;              además a la unidad para que acelere.
  1113.  
  1114. reset_drv      PROC
  1115.                XPUSHA
  1116.                CALL  motor_off_cnt     ; cuenta detención motor
  1117.                MOV   CL,unidad
  1118.                MOV   AL,CL
  1119.                XSHL  AL,2              ; unidad seleccionada
  1120.                OR    AL,1              ; bit de motor
  1121.                SHL   AL,CL             ; colocar dicho bit
  1122.                PUSH  DS                ; *
  1123.                MOV   DX,40h
  1124.                MOV   DS,DX
  1125.                CLI
  1126.                MOV   DS:[3Fh],AL
  1127.                AND   BYTE PTR DS:[3Eh],70h ; bit IRQ=0 y recalibrar
  1128.                POP   DS                ; *
  1129.                XSHL  AL,4              ; bits motor en nibble alto
  1130.                OR    AL,CL             ; seleccionar unidad
  1131.                OR    AL,00001000b      ; interrupciones+DMA y reset
  1132.                MOV   DX,3F2h           ; registro de salida digital
  1133.                OUT   DX,AL             ; señal de reset
  1134.                OR    AL,00000100b
  1135.                MOV   CX,50
  1136. respiro:       LOOP  respiro
  1137.                OUT   DX,AL             ; fin de señal de reset
  1138.                CALL  espera_int        ; rehabilitará interrupciones
  1139.                MOV   AL,8
  1140.                CALL  fdc_write         ; comando 'leer estado int...'
  1141.                CALL  fdc_read
  1142.                CALL  fdc_read
  1143.                CALL  envia_specify     ; comando 'specify' adecuado
  1144.                XPOPA
  1145.                RET
  1146. reset_drv      ENDP
  1147.  
  1148. ; ------------ Enviar comando specify a la controladora. El step-rate
  1149. ;              se selecciona según la densidad, para evitar un sonido
  1150. ;              extraño al posicionar o recalibrar el cabezal.
  1151.  
  1152. envia_specify  PROC
  1153.                PUSH  AX
  1154.                PUSH  DS
  1155.                MOV   AX,40h
  1156.                MOV   DS,AX
  1157.                MOV   AH,DS:[8Bh]
  1158.                POP   DS
  1159.                MOV   AL,3              ; comando 'specify'
  1160.                CALL  fdc_write
  1161.                MOV   AL,0BFh           ; step rate para 500 kbps
  1162.                AND   AH,11000000b
  1163.                JZ    spec1_ok
  1164.                MOV   AL,0AFh           ; step rate para 1 Mbps
  1165.                CMP   AH,11000000b
  1166.                JE    spec1_ok
  1167.                MOV   AL,0DFh           ; step rate para 250/300 Kbps
  1168. spec1_ok:      CALL  fdc_write
  1169.                MOV   AL,2
  1170.                CALL  fdc_write         ; head load y modo DMA
  1171.                POP   AX
  1172.                RET
  1173. envia_specify  ENDP
  1174.  
  1175. ; ------------ Recargar cuenta para la detención del motor. Si CF=1 al
  1176. ;              entrar, se establece la mayor cuenta posible; en caso
  1177. ;              contrario, se pone el valor normal de la tabla base.
  1178.  
  1179. motor_off_cnt  PROC
  1180.                XPUSH <AX, BX>
  1181.                PUSH  DS
  1182.                MOV   AL,0FFh           ; valor máximo
  1183.                JC    motor_off_ok
  1184.                XOR   BX,BX
  1185.                MOV   DS,BX
  1186.                LDS   BX,DWORD PTR DS:[1Eh*4] ; DS:BX -> INT 1Eh
  1187.                MOV   AL,[BX+2]               ; byte 2 tabla base disco
  1188. motor_off_ok:  MOV   BX,40h
  1189.                MOV   DS,BX
  1190.                MOV   BYTE PTR DS:[40h],AL  ; cuenta parada motor
  1191.                POP   DS
  1192.                XPOP  <BX, AX>
  1193.                RET
  1194. motor_off_cnt  ENDP
  1195.  
  1196. ; ------------ Llevar el cabezal a la pista indicada, recalibrando si
  1197. ;              hubo un reset (se invocó la función 0 de la INT 13h o
  1198. ;              se ejecutó reset_drv) antes de esta operación. Primero
  1199. ;              se selecciona la velocidad de transferencia y se borra
  1200. ;              el resultado de cualquier operación anterior, para que
  1201. ;              todo quede listo para el próximo acceso a disco.
  1202.  
  1203. seek_drv       PROC
  1204.                XPUSHA
  1205.                CALL  set_rate          ; velocidad / borrar resultados
  1206.                CALL  envia_specify     ; comando 'specify' adecuado
  1207.                MOV   AH,1
  1208.                MOV   CL,unidad
  1209.                SHL   AH,CL             ; AH = 1 (A:) ó 2 (B:)
  1210.                PUSH  DS
  1211.                MOV   CX,40h
  1212.                MOV   DS,CX
  1213.                TEST  AH,DS:[3Eh]
  1214.                POP   DS
  1215.                JNZ   do_seek           ; la unidad ya fue recalibrada
  1216.                CALL  recalibrar
  1217.                JC    fallo_seek        ; fallo al recalibrar
  1218. do_seek:       MOV   BX,94h
  1219.                ADD   BL,unidad
  1220.                MOV   AL,cilindro
  1221.                PUSH  DS                ; *
  1222.                MOV   CX,40h
  1223.                MOV   DS,CX
  1224.                OR    DS:[3Eh],AH       ; unidad ya recalibrada
  1225.                MOV   AH,DS:[41h]       ; código de error previo
  1226.                CMP   AL,[BX]
  1227.                MOV   [BX],AL
  1228.                POP   DS                ; *
  1229.                JNE   hacer_seek        ; seek necesario
  1230.                CMP   AH,40h            ; ¿error de seek previo?
  1231.                JNE   seek_ok           ; no, evitar seek innecesario
  1232. hacer_seek:    MOV   AL,0Fh
  1233.                CALL  fdc_write         ; comando 'seek'
  1234.                JC    fallo_seek
  1235.                MOV   AL,cabezal
  1236.                XSHL  AL,2
  1237.                OR    AL,unidad
  1238.                CALL  fdc_write         ; enviar HD, US1, US0
  1239.                MOV   AL,cilindro
  1240.                CALL  fdc_write         ; enviar cilindro
  1241.                CALL  espera_int        ; esperar interrupción
  1242.                JC    fallo_seek
  1243.                MOV   AL,8
  1244.                CALL  fdc_write         ; comando 'leer estado int...'
  1245.                JC    fallo_seek
  1246.                CALL  fdc_read          ; leer registro de estado 0
  1247.                JC    fallo_seek
  1248.                MOV   AH,AL
  1249.                CALL  fdc_read          ; leer cilindro actual
  1250.                TEST  AH,11000000b      ; comprobar ST0
  1251.                JNZ   fallo_seek
  1252.                MOV   AL,15             ; estabilización para escritura
  1253.                CMP   orden,F_WRITE
  1254.                JE    rseek_ok
  1255.                MOV   AL,1              ; estabilización para lectura
  1256. rseek_ok:      CBW                     ; AH = 0
  1257.                CALL  retardo           ; esperar asentamiento cabezal
  1258. seek_ok:       CLC                     ; retornar con éxito
  1259.                JMP   seek_ret
  1260. fallo_seek:    STC                     ; retornar indicando fallo
  1261. seek_ret:      XPOPA
  1262.                RET
  1263. seek_drv       ENDP
  1264.  
  1265. ; ------------ Establecer velocidad de transferencia correcta si aún
  1266. ;              no ha sido seleccionada y borrar el resultado de otra
  1267. ;              operación previa.
  1268.  
  1269. set_rate       PROC
  1270.                XPUSHA
  1271.                CALL  pista0?
  1272.                MOV   AX,[SI].vunidad   ; velocidad pista 0 / demás
  1273.                JZ    vel_ok
  1274.                MOV   AL,AH
  1275. vel_ok:        PUSH  DS                ; *
  1276.                MOV   CX,40h
  1277.                MOV   DS,CX
  1278.                MOV   AH,DS:[8Bh]
  1279.                MOV   CL,6
  1280.                SHR   AH,CL             ; aislar bits de velocidad
  1281.                CMP   AL,AH
  1282.                JE    vel_set           ; velocidad ya seleccionada
  1283.                MOV   DX,3F7h
  1284.                OUT   DX,AL             ; seleccionarla
  1285.                SHL   AL,CL
  1286.                AND   BYTE PTR DS:[8Bh],00111111b
  1287.                OR    DS:[8Bh],AL
  1288. vel_set:       POP   DS                ; *
  1289.                LEA   DI,status
  1290.                MOV   CX,8
  1291. borra_status:  MOV   [DI],CH           ; borrar información de estado
  1292.                INC   DI
  1293.                LOOP  borra_status
  1294.                XPOPA
  1295.                RET
  1296. set_rate       ENDP
  1297.  
  1298. ; ------------ Recalibrar la unidad (si hay error se intenta otra vez
  1299. ;              para el caso de que deba moverse más de 77 pistas).
  1300.  
  1301. recalibrar     PROC
  1302.                XPUSHA
  1303.                MOV   BX,94h
  1304.                ADD   BL,unidad
  1305.                PUSH  DS                ; *
  1306.                MOV   CX,40h
  1307.                MOV   DS,CX
  1308.                MOV   [BX],BH           ; pista actual = 0
  1309.                POP   DS                ; *
  1310.                MOV   CX,2              ; dos veces como mucho
  1311. recalibra:     MOV   AL,7
  1312.                CALL  fdc_write         ; comando de 'recalibrado'
  1313.                JC    fallo_recal
  1314.                MOV   AL,cabezal
  1315.                XSHL  AL,2
  1316.                OR    AL,unidad
  1317.                CALL  fdc_write         ; enviar HD, US1, US0
  1318.                JC    fallo_recal
  1319.                CALL  espera_int        ; esperar interrupción
  1320.                JC    fallo_recal
  1321.                MOV   AL,8
  1322.                CALL  fdc_write         ; comando 'leer estado int...'
  1323.                JC    fallo_recal
  1324.                CALL  fdc_read          ; leer registro de estado 0
  1325.                JC    fallo_recal
  1326.                MOV   AH,AL
  1327.                CALL  fdc_read          ; leer cilindro actual
  1328.                XOR   AH,00100000b      ; bajar bit de 'seek end'
  1329.                TEST  AH,11110000b      ; comprobar resultado y ST0
  1330.                JNZ   fallo_recal       ; sin 'seek end' o TRK0
  1331.                MOV   AX,1              ; pausa de 1 ms
  1332.                CALL  retardo
  1333.                JMP   recal_ret
  1334. fallo_recal:   LOOP  recalibra         ; reintentar comando
  1335.                STC                     ; condición de fallo
  1336. recal_ret:     XPOPA
  1337.                RET
  1338. recalibrar     ENDP
  1339.  
  1340. ; ------------ Cargar o escribir sector(es) del disco en ES:DI,
  1341. ;              actualizando la dirección en ES:DI pero sin alterar
  1342. ;              ningún otro registro. Si hay error se devuelve CF=1 y
  1343. ;              no se modifica ES:DI. A partir de fdc_result se dejan
  1344. ;              los 7 bytes que devuelve el FDC al final del acceso.
  1345. ;              En caso de verificación (F_VERIFY) se programa el DMA
  1346. ;              para que no realice transferencia física (convenio de
  1347. ;              las BIOS con fecha 15/11/85 y posterior).
  1348.  
  1349. sector_io      PROC
  1350.                XPUSH <AX, BX, CX, DX>
  1351.                MOV   CL,tsector
  1352.                MOV   CH,0
  1353.                STC
  1354.                RCL   CH,CL
  1355.                MOV   CL,0              ; nº de bytes por sector
  1356.                MOV   AL,sector_fin
  1357.                SUB   AL,sector_ini
  1358.                INC   AX
  1359.                CBW                     ; AX sectores (AH = 0)
  1360.                MUL   CX
  1361.                MOV   DX,AX             ; bytes totales
  1362.                MOV   CX,AX
  1363.                DEC   CX                ; bytes totales - 1
  1364.                MOV   AX,ES
  1365.                CALL  calc_dir_DMA      ; AX:DI -> base BX y página AH
  1366.                MOV   AL,orden          ; modo DMA necesario
  1367.                CALL  prepara_DMA
  1368.                CMP   AL,F_WRITE
  1369.                MOV   AL,11000101b      ; comando de escritura del FDC
  1370.                JE    orden_io_ok
  1371.                MOV   AL,11100110b      ; comando leer (verif.) del FDC
  1372. orden_io_ok:   CALL  fdc_write         ; comando leer/escribir del FDC
  1373.                JC    sector_io_ko
  1374.                MOV   AL,cabezal
  1375.                XSHL  AL,2
  1376.                OR    AL,unidad
  1377.                CALL  fdc_write         ; byte 1 de la orden
  1378.                MOV   AL,cilindro
  1379.                CALL  fdc_write         ; enviar cilindro
  1380.                MOV   AL,cabezal
  1381.                CALL  fdc_write         ; enviar cabezal
  1382.                MOV   AL,sector_ini
  1383.                CALL  fdc_write         ; enviar nº sector
  1384.                MOV   AL,tsector
  1385.                CALL  fdc_write         ; longitud sector
  1386.                MOV   AL,sector_fin
  1387.                CALL  fdc_write         ; último sector
  1388.                MOV   AL,[SI].gap
  1389.                CALL  fdc_write         ; GAP de lectura/escritura
  1390.                MOV   AL,128
  1391.                CALL  fdc_write         ; tamaño sector si longitud=0
  1392.                CALL  espera_int
  1393.                PUSHF                   ; *
  1394.                LEA   BX,fdc_result
  1395.                MOV   CX,7
  1396. sect_io_res:   CALL  fdc_read          ; leyendo resultados
  1397.                MOV   [BX],AL
  1398.                INC   BX
  1399.                LOOP  sect_io_res
  1400.                POPF                    ; *
  1401.                JC    sector_io_ko
  1402.                TEST  fdc_result,11000000b
  1403.                JNZ   sector_io_ko
  1404.                ADD   DI,DX             ; actualizar dirección
  1405.                CLC                     ; Ok
  1406.                JMP   sector_io_fin
  1407. sector_io_ko:  STC                     ; indicar fallo
  1408. sector_io_fin: XPOP  <DX, CX, BX, AX>
  1409.                RET
  1410. sector_io      ENDP
  1411.  
  1412. ; ------------ Devolver en AH la página de DMA y en BX la base. A la
  1413. ;              entrada, AX:DI -> dirección de memoria y CX = bytes-1.
  1414. ;              Se supone que el buffer no cruza una frontera de DMA.
  1415.  
  1416. calc_dir_DMA   PROC
  1417.                PUSH  DX
  1418.                MOV   BX,16
  1419.                MUL   BX
  1420.                ADD   AX,DI
  1421.                ADC   DX,0              ; DX:AX = dirección 20 bits
  1422.                MOV   BX,AX             ; base en BX
  1423.                MOV   AH,DL             ; página
  1424.                POP   DX
  1425.                RET
  1426. calc_dir_DMA   ENDP
  1427.  
  1428. ; ------------ Crear tabla con información para formatear. En ES:BX
  1429. ;              está el futuro sector de arranque del disquete.
  1430.  
  1431. genera_info    PROC
  1432.                XPUSHA
  1433.                MOV   buf_unidad,-1     ; invalidar contenido buffer
  1434.                MOV   SI,buffer
  1435.                MOV   DI,BX
  1436.                CALL  pista0?
  1437.                JNZ   no_cilcab0             ; no es cilindro/cabezal 0
  1438.                ADD   DI,ES:[BX+70]          ; DI -> datos pista 0
  1439.                MOV   CL,ES:[DI]
  1440.                MOV   CH,0                   ; CX sectores en pista 0
  1441.                INC   DI
  1442.                MOV   AL,ES:[DI]             ; GAP para pista 0
  1443.                MOV   AH,0                   ; byte de relleno
  1444.                INC   DI
  1445.                MOV   BYTE PTR [SI],2        ; tamaño de sector
  1446.                MOV   BYTE PTR [SI+1],CL     ; número de sectores
  1447.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1448. genera_0:      ADD   SI,4
  1449.                MOV   AL,cilindro
  1450.                MOV   AH,cabezal
  1451.                MOV   [SI],AX                ; datos para cada sector
  1452.                MOV   AL,ES:[DI]
  1453.                MOV   AH,2                   ; LOG2 (tamaño)-7
  1454.                INC   DI
  1455.                MOV   [SI+2],AX              ; nº de sector / tamaño
  1456.                LOOP  genera_0
  1457.                JMP   geninf_exit
  1458. no_cilcab0:    ADD   DI,ES:[BX+72]
  1459.                CMP   BYTE PTR ES:[BX+65],1
  1460.                JE    info_stv
  1461.                MOV   DL,ES:[DI+2]           ; tamaño /F
  1462.                MOV   DH,ES:[DI]             ; nº sectores
  1463.                MOV   [SI],DX
  1464.                XCHG  DH,DL                  ; tamaño en DH
  1465.                MOV   CL,DL
  1466.                MOV   CH,0                   ; CX sectores
  1467.                MOV   AL,ES:[DI+1]           ; GAP para formatear
  1468.                MOV   AH,0                   ; byte relleno /F
  1469.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1470.                PUSH  DX
  1471.                MOV   AX,ES:[DI+3]
  1472.                PUSH  AX
  1473.                ADD   AL,AH
  1474.                MUL   cilindro
  1475.                MOV   DX,AX
  1476.                POP   AX
  1477.                MUL   cabezal
  1478.                ADD   AX,DX
  1479.                XOR   DX,DX
  1480.                MOV   BL,ES:[DI]
  1481.                MOV   BH,0
  1482.                DIV   BX                ; DL = módulo
  1483.                SUB   DL,ES:[DI]
  1484.                NEG   DL
  1485.                MOV   AL,DL
  1486.                POP   DX                ; restaurar tamaño en DH
  1487.                MOV   DL,AL             ; primer sector de la pista - 1
  1488.                MOV   BL,ES:[DI]        ; nº sectores en la pista
  1489. genera_pn:     ADD   SI,4
  1490.                INC   DX
  1491.                CMP   DL,BL
  1492.                JBE   ns_ok
  1493.                MOV   DL,1              ; empezar desde el 1
  1494. ns_ok:         MOV   AL,cilindro
  1495.                MOV   AH,cabezal
  1496.                MOV   [SI],AX           ; datos para cada sector
  1497.                MOV   [SI+2],DX         ; nº sector / LOG2 (tamaño)-7
  1498.                LOOP  genera_pn
  1499.                JMP   geninf_exit
  1500. info_stv:      MOV   CH,ES:[DI]             ; nº sectores
  1501.                MOV   CL,0                   ; CL:CH sectores
  1502.                MOV   [SI],CX                ; tamaño (CL=0) y número
  1503.                XCHG  CH,CL                  ; CX sectores
  1504.                MOV   AL,ES:[DI+1]           ; GAP para formatear
  1505.                MOV   AH,4Eh                 ; byte de relleno /M
  1506.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1507.                MOV   DL,128
  1508. genera_otro:   ADD   SI,4
  1509.                INC   DX
  1510.                MOV   AL,cilindro
  1511.                MOV   AH,cabezal
  1512.                MOV   [SI],AX           ; datos para cada sector
  1513.                XPUSH <CX, DI>          ; *
  1514.                MOV   CL,ES:[DI+2]      ; CH está a 0
  1515. busca_num:     ADD   DI,3
  1516.                CMP   DL,ES:[DI]
  1517.                MOV   AX,ES:[DI+1]      ; número de sector / tamaño
  1518.                JE    hallado           ; es sector a cambiar número
  1519.                LOOP  busca_num
  1520.                MOV   AL,DL             ; no cambiar número
  1521.                MOV   AH,0              ; e indicar tamaño 128
  1522. hallado:       XPOP  <DI, CX>          ; *
  1523.                MOV   [SI+2],AX         ; nº sector / LOG2 (tamaño)-7
  1524.                LOOP  genera_otro
  1525. geninf_exit:   XPOPA
  1526.                RET
  1527. genera_info    ENDP
  1528.  
  1529. ; ------------ Formatear una pista.
  1530.  
  1531. formatea_pista PROC
  1532.                XPUSHA
  1533.                MOV   BX,buffer
  1534.                MOV   DI,BX
  1535.                MOV   CL,[BX+1]
  1536.                MOV   CH,0              ; CX sectores
  1537.                XSHL  CX,2
  1538.                DEC   CX                ; nº de bytes - 1
  1539.                MOV   AX,DS
  1540.                CALL  calc_dir_DMA      ; AX:DI -> base BX y página AH
  1541.                MOV   AL,4Ah            ; modo DMA para escribir
  1542.                ADD   BX,4              ; saltar primeros 4 bytes
  1543.                CALL  prepara_DMA
  1544.                MOV   BX,buffer
  1545.                MOV   AL,F_FORMAT
  1546.                CALL  fdc_write
  1547.                JC    fallo_fmt
  1548.                MOV   AL,cabezal
  1549.                XSHL  AL,2
  1550.                OR    AL,unidad
  1551.                CALL  fdc_write         ; byte 1 de la orden
  1552.                JC    fallo_fmt
  1553.                MOV   CX,4
  1554. format_cmd:    MOV   AL,[BX]
  1555.                CALL  fdc_write
  1556.                INC   BX
  1557.                LOOP  format_cmd
  1558.                CALL  espera_int
  1559. fallo_fmt:     PUSHF
  1560.                LEA   BX,fdc_result
  1561.                MOV   CX,7
  1562. format_res:    CALL  fdc_read          ; leyendo resultados
  1563.                MOV   [BX],AL
  1564.                INC   BX
  1565.                LOOP  format_res
  1566.                POPF
  1567.                JC    fallo_format
  1568.                TEST  fdc_result,11000000b
  1569.                JZ    format_ret
  1570. fallo_format:  STC                     ; fallo
  1571. format_ret:    XPOPA
  1572.                RET
  1573. formatea_pista ENDP
  1574.  
  1575. ; ------------ Esperar interrupción de disquete durante casi 2
  1576. ;              segundos antes de considerar que ha sido un fracaso.
  1577.  
  1578. espera_int     PROC
  1579.                STI
  1580.                XPUSHA
  1581.                XPUSH <DS, 40h>
  1582.                POP   DS
  1583.                MOV   AH,0FFh
  1584. esperar_int:   CMP   AL,DS:[6Ch]
  1585.                JE    mira_int
  1586.                MOV   AL,DS:[6Ch]
  1587.                INC   AH
  1588.                CMP   AH,37             ; ¿más de 2 segundos?
  1589.                JB    mira_int
  1590. timeout_int:   OR    CS:status,80h     ; timeout
  1591.                STC
  1592.                JMP   fin_espera
  1593. mira_int:      TEST  BYTE PTR DS:[3Eh],80h
  1594.                JZ    esperar_int
  1595.                AND   BYTE PTR DS:[3Eh],7Fh  ; CF=0
  1596. fin_espera:    POP   DS
  1597.                XPOPA
  1598.                RET
  1599. espera_int     ENDP
  1600.  
  1601. ; ------------ Preparar DMA para E/S. A la entrada, BX = dirección de
  1602. ;              base, AH = registro de página y CX = nº bytes - 1.
  1603.  
  1604. prepara_DMA    PROC
  1605.                PUSH  AX
  1606.                CLI
  1607.                OUT   0Bh,AL            ; registro de modo del DMA
  1608.                MOV   AL,0
  1609.                DELAY
  1610.                OUT   0Ch,AL            ; clear first/last flip-flop
  1611.                MOV   AL,BL
  1612.                DELAY
  1613.                OUT   4,AL
  1614.                MOV   AL,BH
  1615.                DELAY
  1616.                OUT   4,AL              ; enviada dirección base
  1617.                DELAY
  1618.                MOV   AL,AH
  1619.                OUT   81h,AL            ; registro de página del DMA
  1620.                MOV   AL,CL
  1621.                DELAY
  1622.                OUT   5,AL
  1623.                MOV   AL,CH
  1624.                DELAY
  1625.                OUT   5,AL              ; enviada cuenta de bytes
  1626.                STI
  1627.                MOV   AL,2
  1628.                DELAY
  1629.                OUT   0Ah,AL            ; habilitar canal 2 de DMA
  1630.                POP   AX
  1631.                RET
  1632. prepara_DMA    ENDP
  1633.  
  1634. ; ------------ Recibir byte del FDC en AL. A la vuelta, CF=1 si
  1635. ;              la operación fracasó (el FDC no estaba listo) y
  1636. ;              se indica la condición de timeout en «status».
  1637.  
  1638. fdc_read       PROC
  1639.                XPUSH <CX, DX>
  1640.                MOV   DX,3F4h           ; registro de estado del FDC
  1641.                XOR   CX,CX             ; evitar cuelgue total si falla
  1642. espera_rd:     IN    AL,DX             ; leer registro de estado
  1643.                TEST  AL,80h            ; ¿bit 7 inactivo?
  1644.                LOOPZ espera_rd         ; así es: el FDC está ocupado
  1645.                JCXZ  fdc_rd_nok
  1646.                INC   DX                ; apuntar al registro de datos
  1647.                IN    AL,DX             ; leer byte del FDC
  1648.                CLC
  1649.                XPOP  <DX, CX>
  1650.                RET
  1651. fdc_rd_nok:    OR    status,80h        ; timeout
  1652.                STC
  1653.                XPOP  <DX, CX>
  1654.                RET
  1655. fdc_read       ENDP
  1656.  
  1657. ; ------------ Enviar byte AL al FDC. A la vuelta, CF=1 si
  1658. ;              la operación fracasó (el FDC no estaba listo) y
  1659. ;              se indica la condición de timeout en «status».
  1660.  
  1661. fdc_write      PROC
  1662.                XPUSH <AX, CX, DX>
  1663.                MOV   DX,3F4h           ; registro de estado del FDC
  1664.                XCHG  AH,AL             ; preservar AL en AH
  1665.                XOR   CX,CX             ; evitar cuelgue total si falla
  1666. espera_wr:     IN    AL,DX             ; leer registro de estado
  1667.                TEST  AL,80h            ; ¿bit 7 inactivo?
  1668.                LOOPZ espera_wr         ; así es: el FDC está ocupado
  1669.                JCXZ  fdc_wr_nok
  1670.                XCHG  AH,AL             ; recuperar el dato de AL
  1671.                INC   DX                ; apuntar al registro de datos
  1672.                OUT   DX,AL             ; enviar byte al FDC
  1673.                XPOP  <DX, CX, AX>
  1674.                CLC
  1675.                RET
  1676. fdc_wr_nok:    OR    status,80h        ; timeout
  1677.                XPOP  <DX, CX, AX>
  1678.                STC
  1679.                RET
  1680. fdc_write      ENDP
  1681.  
  1682. ; ------------ Esperar exactamente AX milisegundos.
  1683.  
  1684. retardo        PROC
  1685.                PUSHF
  1686.                XPUSH <AX, BX, CX, DX>
  1687. retarda_mas:   CMP   AX,54             ; como máximo 54 ms cada vez
  1688.                JBE   retarda_fin
  1689.                PUSH  AX
  1690.                MOV   AX,54
  1691.                CALL  rt_ax
  1692.                POP   AX
  1693.                SUB   AX,54
  1694.                JMP   retarda_mas
  1695. retarda_fin:   CALL  rt_ax
  1696.                XPOP  <DX, CX, BX, AX>
  1697.                POPF
  1698.                RET
  1699.  
  1700. rt_ax:         MOV   DX,1000           ; retardo de hasta 54 ms
  1701.                MUL   DX
  1702.                MUL   CS:tbase
  1703.                MOV   CX,54925
  1704.                DIV   CX                ; AX = contador iteraciones
  1705.                MOV   CX,AX
  1706.                EVEN                    ; forzar alineamiento
  1707. retarda:       DEC   CX
  1708.                JMP   SHORT $+2
  1709.                JNZ   retarda
  1710.                RET
  1711. retardo        ENDP
  1712.  
  1713.                ; --- Ubicación del sector de hasta 2048 bytes.
  1714.  
  1715.                EVEN
  1716. buffer_io      EQU   $
  1717. tbuffer        EQU   2048
  1718.  
  1719. fin_residente  EQU   $  ; fin del área residente sin contar el buffer
  1720.  
  1721. bytes_resid    EQU   fin_residente-ini_residente
  1722.  
  1723.  
  1724. ; *****************************
  1725. ; *                           *
  1726. ; *   I N S T A L A C I O N   *
  1727. ; *                           *
  1728. ; *****************************
  1729.  
  1730. main           PROC
  1731.                CALL  obtener_param     ; analizar posibles parámetros
  1732.                JNC   params_ok         ; son correctos
  1733.                OR    error,MALSINTAX
  1734.                JMP   informar
  1735. params_ok:     CALL  inic_general      ; inicializar ciertas variables
  1736.                CALL  pc_ok?
  1737.                TEST  error,MALPC or MALDOS or MALBIOS or MALDRV
  1738.                JZ    proc_tsr
  1739.                JMP   informar          ; no hay configuración correcta
  1740. proc_tsr:      CMP   param_u,ON        ; ¿se solicita desinstalarlo?
  1741.                JNE   cont_ins          ; no
  1742.                CALL  residente?        ; sí: ¿está residente?
  1743.                JNC   desinstalable
  1744.                OR    error,NOINSTALADO ; programa aún no instalado
  1745.                JMP   informar
  1746. desinstalable: CALL  testWin
  1747.                JNC   win_out_ok
  1748.                OR    error,WIN_UNLOAD
  1749.                JMP   informar          ; no desinstalar desde Windows
  1750. win_out_ok:    OR    accion,DESINSTALADO
  1751.                MOV   ES,tsr_seg
  1752.                MOV   DI,tsr_off
  1753.                TEST  BYTE PTR ES:[DI-10],2
  1754.                JZ    no_sys
  1755.                OR    error,INSTALADOSYS  ; no desinstalar versión SYS
  1756.                JMP   informar
  1757. no_sys:        MOV   AH,ES:[DI-9]
  1758.                CALL  mx_unload         ; desinstalarlo:
  1759.                JNC   informar          ; ha sido posible
  1760.                OR    error,IMPDESINS   ; no es posible
  1761.                JMP   informar
  1762. cont_ins:      CALL  residente?
  1763.                JNC   ya_reside
  1764.                CMP   AX,0              ; ¿reside una versión distinta?
  1765.                JE    instalable
  1766.                OR    error,NOINSTALABLE  ; versión incompatible
  1767.                JMP   informar
  1768. ya_reside:     OR    accion,YAINSTALADO
  1769.                CALL  adaptar_param     ; parámetros en copia residente
  1770.                JMP   informar
  1771. instalable:    CALL  testWin
  1772.                JNC   win_in_ok
  1773.                OR    error,WIN_LOAD
  1774.                JMP   informar          ; no instalar desde Windows
  1775. win_in_ok:     OR    accion,INSTALADO
  1776.                CALL  instalar_tsr      ; instalar el TSR
  1777.                MOV   ES,tsr_seg
  1778. informar:      CALL  info
  1779.                MOV   DX,mem640         ; tamaño zona residente
  1780.                AND   DX,DX
  1781.                JZ    fin_noresid
  1782.                MOV   AX,3100h
  1783.                INT   21h               ; terminar residente
  1784. fin_noresid:   MOV   AX,4C00h
  1785.                INT   21h               ; terminar no residente
  1786. main           ENDP
  1787.  
  1788. instalar_tsr   PROC
  1789.                CALL  mx_get_handle     ; obtener entrada Multiplex
  1790.                JNC   handle_ok
  1791.                OR    error,MX64FULL    ; no quedan entradas
  1792.                RET
  1793. handle_ok:     MOV   multiplex_id,AH   ; entrada multiplex para 2M
  1794.                CALL  preservar_ints    ; tomar nota de vectores
  1795.                CMP   param_ml,ON       ; ¿se indicó parámetro /ML?
  1796.                JE    instalar_ml       ; en efecto
  1797.                MOV   AX,parrafos_resid ; área residente sin PSP
  1798.                CALL  UMB_alloc         ; pedir memoria superior XMS
  1799.                JC    busca_upper       ; no hay la suficiente
  1800.                LEA   BX,buffer_io-256  ; AX:BX dirección del buffer
  1801.                CALL  testDMA
  1802.                PUSH  AX
  1803.                MOV   DX,AX
  1804.                MOV   AH,12h
  1805.                CALL  gestor_XMS        ; redimensionar bloque memoria
  1806.                CMP   AX,1
  1807.                JE    xms_redim_ok
  1808.                MOV   AX,mem640
  1809.                MOV   parrafos_resid,AX ; función no soportada
  1810. xms_redim_ok:  POP   AX
  1811.                CLC                     ; indicar que usa memoria XMS
  1812.                JMP   instalar_umb
  1813. busca_upper:   MOV   AX,parrafos_resid
  1814.                CALL  UPPER_alloc       ; pedir memoria superior DOS 5
  1815.                JC    instalar_ml       ; no hay la suficiente
  1816.                LEA   BX,buffer_io-256  ; AX:BX dirección del buffer
  1817.                CALL  testDMA
  1818.                XPUSH <AX, ES>
  1819.                MOV   ES,AX
  1820.                MOV   AH,4Ah
  1821.                INT   21h               ; redimensionar bloque memoria
  1822.                CALL  upper_fork        ; dejar residente el bloque
  1823.                XPOP  <ES, AX>
  1824.                STC                     ; indicar que usa memoria DOS
  1825. instalar_umb:  MOV   ES,AX             ; segmento del bloque UMB
  1826.                MOV   DI,0              ; ES:0 zona a donde reubicar
  1827.                MOV   mem640,DI         ; no terminar residente
  1828.                JMP   instalar_asi
  1829. instalar_ml:   ADD   parrafos_resid,6  ; respetar 96 bytes de PSP
  1830.                MOV   AX,CS
  1831.                LEA   BX,buffer_io-(256-96)
  1832.                CALL  testDMA
  1833.                MOV   mem640,BX         ; ocupar memoria convencional
  1834.                STC
  1835.                MOV   DI,96             ; instalación mem. convencional
  1836.                CALL  free_environ      ; liberar espacio de entorno
  1837. instalar_asi:  CALL  inicializa_id     ; inicializar identificación
  1838.                CALL  reubicar_prog     ; reubicar programa a ES:DI
  1839.                CALL  activar_ints      ; interceptar vectores
  1840.                RET
  1841. instalar_tsr   ENDP
  1842.  
  1843. ;*********************************************************
  1844. ;*                                                       *
  1845. ;*  SUBRUTINAS DE PROPOSITO GENERAL PARA LA INSTALACION  *
  1846. ;*                                                       *
  1847. ;*********************************************************
  1848.  
  1849. ; ------------ Extraer posibles parámetros de la línea de comandos.
  1850.  
  1851. obtener_param  PROC
  1852.                CALL  param_mays        ; mayusculizar parámetros
  1853.                MOV   BX,81h            ; apuntar a zona de parámetros
  1854. otro_pmt_mas:  CALL  saltar_esp        ; saltar delimitadores
  1855.                JNC   otro_pmt          ; quedan más parámetros
  1856.                CMP   param_ayuda,ON
  1857.                JE    mal_proc_pmt      ; 'error' de ayuda
  1858.                CLC                     ; parámetros bien procesados
  1859.                RET
  1860. otro_pmt:      CMP   AL,'/'
  1861.                JE    pmt_barrado       ; parámetro precedido por '/'
  1862.                CMP   AL,'?'
  1863.                JNE   mal_proc_pmt
  1864. pmt_hlp:       MOV   param_ayuda,ON
  1865.                INC   BX
  1866.                JMP   otro_pmt_mas
  1867. pmt_barrado:   INC   BX
  1868.                MOV   AL,[BX]           ; letra del parámetro
  1869.                CMP   AL,13             ; ¿fin de mandatos?
  1870.                JE    mal_proc_pmt      ; falta parámetro
  1871.                CMP   AL,'?'
  1872.                JE    pmt_hlp
  1873.                CMP   AL,'H'
  1874.                JE    pmt_hlp
  1875.                CMP   AL,'U'
  1876.                JE    pmt_U             ; parámetro /U
  1877.                CMP   AL,'I'
  1878.                JE    pmt_I             ; parámetro /I
  1879.                CMP   AL,'W'
  1880.                JE    pmt_W             ; parámetro /W
  1881.                MOV   SI,[BX]           ; ¿parámetro de dos caracteres?
  1882.                CMP   SI,"LM"
  1883.                JE    pmt_ML            ; parámetro /ML
  1884. mal_proc_pmt:  STC                     ; error en parámetro(s)
  1885.                RET
  1886. pmt_U:         MOV   param_u,ON
  1887.                INC   BX
  1888.                JMP   otro_pmt_mas
  1889. pmt_I:         MOV   param_i,ON
  1890.                INC   BX
  1891.                JMP   otro_pmt_mas
  1892. pmt_W:         MOV   param_w,ON
  1893.                INC   BX
  1894.                JMP   otro_pmt_mas
  1895. pmt_ML:        MOV   param_ml,ON
  1896.                ADD   BX,2
  1897.                JMP   otro_pmt_mas
  1898. obtener_param  ENDP
  1899.  
  1900. ; ------------ Poner en mayúsculas los parámetros.
  1901.  
  1902. param_mays     PROC
  1903.                MOV   BX,81h
  1904. otra_mays:     MOV   AL,[BX]
  1905.                CMP   AL,13
  1906.                JNE   mays_mas
  1907.                RET
  1908. mays_mas:      CMP   AL,'a'
  1909.                JB    mayusc_ok
  1910.                CMP   AL,'z'
  1911.                JA    mayusc_ok
  1912.                SUB   AL,32
  1913. mayusc_ok:     MOV   [BX],AL
  1914.                INC   BX
  1915.                JMP   otra_mays
  1916. param_mays     ENDP
  1917.  
  1918. ; ------------ Saltar espacios, tabuladores,... buscando un parámetro.
  1919.  
  1920. saltar_esp:    MOV   AX,[BX]
  1921.                INC   BX
  1922.                CMP   AL,9              ; carácter tabulador
  1923.                JE    saltar_esp
  1924.                CMP   AL,32             ; espacio en blanco
  1925.                JE    saltar_esp
  1926.                CMP   AL,0Dh            ; fin de zona de parámetros
  1927.                JE    fin_param
  1928.                DEC   BX                ; puntero al primer carácter
  1929.                CLC                     ; hay parámetro
  1930.                RET
  1931. fin_param:     STC                     ; no hay parámetro
  1932.                RET
  1933.  
  1934. ; ------------ Ya está instalada otra versión distinta del programa.
  1935.  
  1936. error_version  PROC
  1937.                PUSH  ES
  1938.                LEA   DX,mal_ver_txt1
  1939.                CALL  print
  1940.                LES   DI,tsr_dir
  1941.                MOV   AL,':'
  1942.                MOV   CL,255
  1943.                CLD
  1944.                REPNE SCASB
  1945.                REPNE SCASB
  1946.                MOV   DL,ES:[DI]        ; número de versión
  1947.                MOV   AH,2
  1948.                INT   21h
  1949.                MOV   DL,'.'
  1950.                MOV   AH,2
  1951.                INT   21h
  1952.                MOV   DL,ES:[DI+2]      ; revisión
  1953.                MOV   AH,2
  1954.                INT   21h
  1955.                LEA   DX,mal_ver_txt2
  1956.                CALL  print
  1957.                POP   ES
  1958.                RET
  1959. error_version  ENDP
  1960.  
  1961. ; ------------ Inicializar ciertas variables. La memoria consumida
  1962. ;              se calcula para el peor de los casos, cuando el buffer
  1963. ;              residente ocupa incluso el doble debido al DMA.
  1964.  
  1965. inic_general   PROC
  1966.                CALL  inic_XMS          ; detectar controlador XMS
  1967.                MOV   AX,(bytes_resid+tbuffer*2+15)/16
  1968.                MOV   parrafos_resid,AX ; memoria máxima necesaria
  1969.                CALL  testAT
  1970.                JNC   skip_tmtest       ; en AT no calcularlo [*]
  1971.                CALL  cte_tiempos
  1972.                MOV   tbase,AX          ; cte. retardo para 1/18,2 seg.
  1973. skip_tmtest:   MOV   DL,0
  1974.                CALL  tipo_disco
  1975.                JC    err_drv
  1976.                AND   DL,DL
  1977.                JZ    err_drv             ; no hay disqueteras
  1978.                MOV   info_A.tipo_drv,AL  ; guardar tipo unidad A:
  1979.                CMP   DL,1
  1980.                JE    fin_inic            ; no existe unidad B:
  1981.                MOV   DL,1
  1982.                CALL  tipo_disco
  1983.                JC    fin_inic
  1984.                MOV   info_B.tipo_drv,AL  ; guardar tipo unidad B:
  1985.                RET
  1986. err_drv:       MOV   AX,MALBIOS
  1987.                OR    error,AX
  1988. fin_inic:      RET
  1989.  
  1990. tipo_disco:    PUSH  ES
  1991.                MOV   AH,8
  1992.                MOV   BL,0
  1993.                INT   13h
  1994.                JC    tipo_dsk_err
  1995.                MOV   AL,BL
  1996.                AND   AL,AL
  1997.                JZ    tipo_dsk_err
  1998.                CMP   AL,4
  1999.                JB    tipo_dsk_ok
  2000.                MOV   AL,4                ; 1.44/2.88 indistinto
  2001. tipo_dsk_ok:   CLC
  2002.                POP   ES
  2003.                RET
  2004. tipo_dsk_err:  STC
  2005.                POP   ES
  2006.                RET
  2007. inic_general   ENDP
  2008.  
  2009. ; ------------ Considerar presencia de controlador XMS.
  2010.  
  2011. inic_XMS       PROC
  2012.                PUSH  ES
  2013.                MOV   AX,352Fh
  2014.                INT   21h               ; dirección de INT 2Fh en ES:BX
  2015.                MOV   AX,ES
  2016.                POP   ES
  2017.                AND   AX,AX
  2018.                JZ    xms_ausente       ; apunta a 0000:XXXX (DOS 2.x)
  2019.                MOV   AX,4300h
  2020.                INT   2Fh               ; chequear presencia XMS
  2021.                CMP   AL,80h
  2022.                JNE   XMS_ausente       ; no instalado
  2023.                PUSH  ES
  2024.                MOV   AX,4310h
  2025.                INT   2Fh               ; sí: obtener su dirección
  2026.                MOV   XMS_off,BX        ; y preservarla
  2027.                MOV   XMS_seg,ES
  2028.                MOV   xms_ins,ON
  2029.                POP   ES
  2030.                RET
  2031. XMS_ausente:   MOV   xms_ins,OFF
  2032.                RET
  2033. inic_XMS       ENDP
  2034.  
  2035. ; ------------ Inicializar área «program_id» del programa residente.
  2036. ;              A la entrada, ES:DI = seg:off a donde será reubicado
  2037. ;              y CF=1 si se utiliza memoria superior XMS.
  2038.  
  2039. inicializa_id  PROC
  2040.                PUSHF
  2041.                MOV   segmento_real,ES  ; anotar segmento del bloque
  2042.                MOV   offset_real,DI    ; ídem con el offset
  2043.                MOV   AX,parrafos_resid
  2044.                MOV   longitud_total,AX
  2045.                MOV   AL,1
  2046.                POPF                    ; CF=0: usar memoria UMB XMS
  2047.                JNC   info_ok
  2048.                DEC   AL                ; usar memoria convencional
  2049. info_ok:       OR    info_extra,AL
  2050.                RET
  2051. inicializa_id  ENDP
  2052.  
  2053. ; ------------ Comprobar si el programa ya reside en memoria. A la
  2054. ;              salida, CF=0 si programa ya reside, con «tsr_seg» y
  2055. ;              «tsr_off» inicializadas apuntando a la cadena de
  2056. ;              identificación de la copia residente.  Si CF=1, el
  2057. ;              programa no reside aún (AX=0) o reside pero en otra
  2058. ;              versión distinta (AX=1).
  2059.  
  2060. residente?     PROC
  2061.                XPUSH <CX, SI, DI, ES, AX>
  2062.                LEA   DI,autor_nom_ver  ; identificación del programa
  2063.                MOV   SI,DI
  2064.                MOV   AL,0
  2065.                MOV   CL,255
  2066.                CLD
  2067.                REPNE SCASB
  2068.                SUB   DI,SI
  2069.                MOV   CX,DI             ; tamaño autor+programa+versión
  2070.                MOV   AX,1492h
  2071.                MOV   ES,AX
  2072.                MOV   DI,1992h          ; ES:DI protocolo de búsqueda
  2073.                CALL  mx_find_tsr       ; buscar si está en memoria
  2074.                MOV   tsr_off,DI        ; anotar la dirección programa
  2075.                MOV   tsr_seg,ES        ; por si estaba instalado
  2076.                POP   AX
  2077.                JNC   resid_ok          ; CF=0 -> programa ya residente
  2078.                POP   ES
  2079.                PUSH  ES
  2080.                LEA   DI,autor_nom_ver
  2081.                MOV   SI,DI
  2082.                MOV   AL,':'
  2083.                MOV   CL,255
  2084.                REPNE SCASB
  2085.                REPNE SCASB
  2086.                SUB   DI,SI
  2087.                MOV   CX,DI             ; tamaño autor+programa
  2088.                MOV   AX,1492h
  2089.                MOV   ES,AX
  2090.                MOV   DI,1992h          ; ES:DI protocolo de búsqueda
  2091.                CALL  mx_find_tsr       ; buscar si está en memoria
  2092.                MOV   tsr_off,DI        ; anotar dirección del programa
  2093.                MOV   tsr_seg,ES        ; por si instalada otra versión
  2094.                MOV   AX,0
  2095.                JC    resid_ok          ; CF=1, AX=0 -> no residente
  2096.                MOV   AX,1
  2097.                STC                     ; CF=1, AX=1 -> sí: otra vers.
  2098. resid_ok:      XPOP  <ES, DI, SI, CX>
  2099.                RET
  2100. residente?     ENDP
  2101.  
  2102. ; ------------ Adaptar parámetros de un 2MX ya instalado.
  2103. ;              Sólo se actualiza la constante de retardo, por si el
  2104. ;              turbo de la máquina fue conmutado.
  2105.  
  2106. adaptar_param  PROC
  2107.                PUSH  ES
  2108.                MOV   ES,tsr_seg
  2109.                MOV   AX,tbase
  2110.                MOV   ES:tbase,AX
  2111.                POP   ES
  2112.                RET
  2113. adaptar_param  ENDP
  2114.  
  2115. ; ------------ Preservar vectores de interrupción previos.
  2116.  
  2117. preservar_INTs PROC
  2118.                XPUSH <ES, DI>
  2119.                LEA   DI,tabla_vectores
  2120.                MOV   CL,[DI-1]
  2121.                MOV   CH,0              ; CX vectores interceptados
  2122. otro_vector:   XPUSH <CX, DI>
  2123.                MOV   AH,35h
  2124.                MOV   AL,[DI]
  2125.                INT   21h               ; obtener vector de INT xx
  2126.                XPOP  <DI, CX>
  2127.                MOV   [DI+1],BX         ; anotar donde apunta
  2128.                MOV   [DI+3],ES
  2129.                ADD   DI,5
  2130.                LOOP  otro_vector       ; repetir con los restantes
  2131.                XPOP  <DI, ES>
  2132.                RET
  2133. preservar_INTs ENDP
  2134.  
  2135. ; ------------ Liberar espacio de entorno.
  2136.  
  2137. free_environ   PROC
  2138.                PUSH  ES
  2139.                MOV   ES,DS:[2Ch]       ; dirección del entorno
  2140.                MOV   AH,49h
  2141.                INT   21h               ; liberar espacio de entorno
  2142.                POP   ES
  2143.                RET
  2144. free_environ   ENDP
  2145.  
  2146. ; ------------ Reservar bloque de memoria superior del nº párrafos AX,
  2147. ;              devolviendo en AX el segmento donde está. CF=1 si no
  2148. ;              está instalado el gestor XMS (AX=0) o hay un error (AL
  2149. ;              devuelve el código de error del controlador XMS).
  2150.  
  2151. UMB_alloc      PROC
  2152.                XPUSH <BX, CX, DX>
  2153.                CMP   xms_ins,ON
  2154.                JNE   no_umb_disp       ; no hay controlador XMS
  2155.                MOV   DX,AX             ; número de párrafos
  2156.                MOV   AH,10h            ; solicitar memoria superior
  2157.                CALL  gestor_XMS
  2158.                CMP   AX,1              ; ¿ha ido todo bien?
  2159.                MOV   AX,BX             ; segmento UMB/código de error
  2160.                JNE   XMS_fallo         ; fallo
  2161.                XPOP  <DX, CX, BX>      ; ok
  2162.                CLC
  2163.                RET
  2164. no_umb_disp:   MOV   AX,0
  2165. XMS_fallo:     XPOP  <DX, CX, BX>
  2166.                STC
  2167.                RET
  2168. UMB_alloc      ENDP
  2169.  
  2170. ; ------------ Reservar memoria superior, con DOS 5.0, del tamaño
  2171. ;              solicitado (AX párrafos). Si no hay bastante CF=1,
  2172. ;              en caso contrario devuelve el segmento en AX.
  2173.  
  2174. UPPER_alloc    PROC
  2175.                PUSH  AX
  2176.                MOV   AH,30h
  2177.                INT   21h
  2178.                CMP   AL,5
  2179.                POP   AX
  2180.                JAE   UPPER_existe
  2181.                STC
  2182.                JMP   UPPER_fin         ; necesario DOS 5.0 mínimo
  2183. UPPER_existe:  PUSH  AX                ; preservar párrafos...
  2184.                MOV   AX,5800h
  2185.                INT   21h
  2186.                MOV   alloc_strat,AX    ; preservar estrategia
  2187.                MOV   AX,5802h
  2188.                INT   21h
  2189.                MOV   umb_state,AL      ; preservar estado UMB
  2190.                MOV   AX,5803h
  2191.                MOV   BX,1
  2192.                INT   21h               ; conectar cadena UMB's
  2193.                MOV   AX,5801h
  2194.                MOV   BX,41h
  2195.                INT   21h               ; High Memory best fit
  2196.                POP   BX                ; ...párrafos requeridos
  2197.                MOV   AH,48h
  2198.                INT   21h               ; asignar memoria
  2199.                PUSHF
  2200.                PUSH  AX                ; guardado el resultado
  2201.                MOV   AX,5801h
  2202.                MOV   BX,alloc_strat
  2203.                INT   21h               ; restaurar estrategia
  2204.                MOV   AX,5803h
  2205.                MOV   BL,umb_state
  2206.                XOR   BH,BH
  2207.                INT   21h               ; restaurar estado cadena UMB
  2208.                POP   AX
  2209.                POPF
  2210. upper_fin:     RET
  2211. UPPER_alloc    ENDP
  2212.  
  2213. ; ------------ Manipular PID para independizar el bloque de memoria
  2214. ;              superior del programa y dejarlo residente. ES apunta
  2215. ;              al segmento y DS al PSP del programa principal.
  2216.  
  2217. upper_fork     PROC
  2218.                XPUSH <DS, ES>
  2219.                MOV   AX,ES
  2220.                DEC   AX
  2221.                MOV   ES,AX
  2222.                INC   AX
  2223.                MOV   WORD PTR ES:[1],AX      ; manipular PID
  2224.                MOV   WORD PTR ES:[16],20CDh  ; simular PSP
  2225.                MOV   AX,DS
  2226.                DEC   AX
  2227.                MOV   DS,AX
  2228.                MOV   CX,8
  2229.                MOV   SI,CX
  2230.                MOV   DI,CX
  2231.                CLD
  2232.                REP   MOVSB             ; copiar nombre de programa
  2233.                XPOP  <ES, DS>
  2234.                RET
  2235. upper_fork     ENDP
  2236.  
  2237. ; ------------ Reubicar programa residente a su dirección definitiva.
  2238.  
  2239. reubicar_prog  PROC
  2240.                PUSH  DI
  2241.                LEA   SI,ini_residente
  2242.                MOV   CX,bytes_resid
  2243.                CLD
  2244.                ADD   SI,2              ; no copiar primera palabra
  2245.                ADD   DI,2              ; respetar primera palabra
  2246.                SUB   CX,2
  2247.                REP   MOVSB
  2248.                POP   DI
  2249.                RET
  2250. reubicar_prog  ENDP
  2251.  
  2252. ; ------------ desviar vectores de interrupción a las nuevas rutinas.
  2253. ;              Se tendrá en cuenta que está ensambladas para correr en
  2254. ;              un offset inicial (100h) y que el offset real en que
  2255. ;              han sido instaladas está en DI. Por ello, CS ha de
  2256. ;              desplazarse (100h-DI)/16 unidades atrás (DI se supone
  2257. ;              múltiplo de 16). El segmento inicial es ES.
  2258.  
  2259. activar_INTs   PROC
  2260.                XPUSH <CX, DS>          ; preservar DS para el retorno
  2261.                MOV   AX,100h
  2262.                SUB   AX,DI             ; AX = 100h-DI
  2263.                MOV   CL,4
  2264.                SHR   AX,CL             ; AX = (100h-DI)/16
  2265.                MOV   CX,ES
  2266.                SUB   CX,AX
  2267.                MOV   tsr_seg,CX
  2268.                MOV   DS,CX
  2269.                LEA   SI,offsets_ints
  2270.                MOV   CX,CS:[SI]        ; CX vectores a desviar
  2271.                ADD   SI,2
  2272. desvia_otro:   MOV   AL,CS:[SI]        ; número del vector en curso
  2273.                MOV   DX,CS:[SI+1]      ; obtener offset
  2274.                MOV   AH,25h
  2275.                INT   21h               ; desviar INT xx a DS:DX
  2276.                ADD   SI,3
  2277.                LOOP  desvia_otro
  2278.                XPOP  <DS, CX>
  2279.                RET
  2280. activar_INTs   ENDP
  2281.  
  2282. ; ------------ Buscar entrada no usada en la interrupción Multiplex.
  2283. ;              A la salida, CF=1 si no hay hueco (ya hay 64 programas
  2284. ;              residentes instalados con esta técnica). Si CF=0, se
  2285. ;              devuelve en AH un valor de entrada libre en la INT 2Fh.
  2286.  
  2287. mx_get_handle  PROC
  2288.                MOV   AH,0C0h
  2289. mx_busca_hndl: PUSH  AX
  2290.                MOV   AL,0
  2291.                INT   2Fh
  2292.                CMP   AL,0FFh
  2293.                POP   AX
  2294.                JNE   mx_si_hueco
  2295.                INC   AH
  2296.                JNZ   mx_busca_hndl
  2297.                STC
  2298.                RET
  2299. mx_si_hueco:   CLC
  2300.                RET
  2301. mx_get_handle  ENDP
  2302.  
  2303. ; ------------ Buscar un TSR por la interrupción Multiplex. A la
  2304. ;              entrada, DS:SI cadena de identificación del programa
  2305. ;              (CX bytes) y ES:DI protocolo de búsqueda (normalmente
  2306. ;              1492h:1992h). A la salida, si el TSR ya está instalado,
  2307. ;              CF=0 y ES:DI apunta a la cadena de identificación del
  2308. ;              mismo. Si no, CF=1 y ningún registro alterado.
  2309.  
  2310. mx_find_tsr    PROC
  2311.                MOV   AH,0C0h
  2312. mx_rep_find:   XPUSH <AX, CX, SI, DS, ES, DI>
  2313.                MOV   AL,0
  2314.                PUSH  CX
  2315.                INT   2Fh
  2316.                POP   CX
  2317.                CMP   AL,0FFh
  2318.                JNE   mx_skip_hndl      ; no hay TSR ahí
  2319.                CLD
  2320.                PUSH  DI
  2321.                REP   CMPSB             ; comparar identificación
  2322.                POP   DI
  2323.                JE    mx_tsr_found      ; programa buscado hallado
  2324. mx_skip_hndl:  XPOP  <DI, ES, DS, SI, CX, AX>
  2325.                INC   AH
  2326.                JNZ   mx_rep_find
  2327.                STC
  2328.                RET
  2329. mx_tsr_found:  ADD   SP,4              ; «sacar» ES y DI de la pila
  2330.                XPOP  <DS, SI, CX, AX>
  2331.                CLC
  2332.                RET
  2333. mx_find_tsr    ENDP
  2334.  
  2335. ; ------------ Eliminar TSR del convenio si es posible. A la entrada,
  2336. ;              en AH se indica la entrada Multiplex; a la salida, CF=1
  2337. ;              si fue imposible y CF=0 si se pudo. Se corrompen todos
  2338. ;              los registros salvo los de segmento. En caso de fallo
  2339. ;              al desinstalar, AL devuelve el vector «culpable».
  2340.  
  2341. mx_unload      PROC
  2342.                PUSH  ES
  2343.                CALL  mx_ul_tsrcv?
  2344.                JNC   mx_ul_able
  2345.                POP   ES
  2346.                RET
  2347. mx_ul_able:    XOR   AL,AL
  2348.                XCHG  AH,AL
  2349.                MOV   BP,AX             ; BP=entrada Multiplex del TSR
  2350.                MOV   CX,2
  2351. mx_ul_pasada:  PUSH  CX                ; siguiente pasada
  2352.                LEA   SI,tabla_vectores
  2353.                MOV   CL,ES:[SI-1]
  2354.                MOV   CH,0              ; CX = nº vectores
  2355. mx_ul_masvect: POP   AX
  2356.                PUSH  AX                ; pasada en curso
  2357.                DEC   AL
  2358.                PUSH  CX
  2359. mx_ul_2f:      MOV   AL,ES:[SI]        ; vector en curso
  2360.                JNZ   mx_ul_pasok
  2361.                CMP   CX,1              ; ¿último vector?
  2362.                JNE   mx_ul_noult
  2363.                MOV   AL,2Fh
  2364.                LEA   SI,tabla_vectores
  2365. mx_ul_busca2f: CMP   ES:[SI],AL        ; ¿INT 2Fh?
  2366.                JE    mx_ul_pasok
  2367.                ADD   SI,5
  2368.                JMP   mx_ul_busca2f
  2369. mx_ul_noult:   CMP   AL,2Fh            ; ¿restaurar INT 2Fh?
  2370.                JNE   mx_ul_pasok
  2371.                ADD   SI,5
  2372.                JMP   mx_ul_2f
  2373. mx_ul_pasok:   XPUSH <ES, AX>
  2374.                MOV   AH,0
  2375.                SHL   AX,1
  2376.                SHL   AX,1
  2377.                DEC   AX
  2378.                MOV   CS:mx_ul_tsroff,AX
  2379.                MOV   CS:mx_ul_tsrseg,0 ; apuntar a tabla vectores
  2380.                POP   AX
  2381.                PUSH  AX
  2382.                MOV   AH,35h
  2383.                INT   21h               ; vector en ES:BX
  2384.                POP   AX
  2385.                MOV   CL,4
  2386.                SHR   BX,CL
  2387.                MOV   DX,ES
  2388.                ADD   DX,BX             ; INT xx en DX (aprox.)
  2389.                MOV   AH,0C0h
  2390. mx_ul_masmx:   CALL  mx_ul_tsrcv?
  2391.                JNC   mx_ul_tsrcv
  2392.                JMP   mx_ul_otro
  2393. mx_ul_tsrcv:   PUSH  ES:[DI-16]        ; ...TSR del convenio en ES:DI
  2394.                PUSH  ES:[DI-12]
  2395.                MOV   DI,ES:[DI-8]      ; offset a la tabla de vectores
  2396.                MOV   CL,ES:[DI-1]
  2397.                MOV   CH,0              ; número de vectores en CX
  2398. mx_ul_buscav:  CMP   AL,ES:[DI]
  2399.                JE    mx_ul_usavect     ; este TSR usa vector analizado
  2400.                ADD   DI,5
  2401.                LOOP  mx_ul_buscav
  2402.                ADD   SP,4              ; no lo usa
  2403.                JMP   mx_ul_otro
  2404. mx_ul_usavect: XPOP  <CX, BX>          ; tamaño y segmento del TSR
  2405.                CMP   DX,BX
  2406.                JB    mx_ul_otro        ; la INT xx no le apunta
  2407.                ADD   BX,CX
  2408.                CMP   DX,BX
  2409.                JA    mx_ul_otro        ; la INT xx le apunta
  2410.                PUSH  AX
  2411.                XOR   AL,AL
  2412.                XCHG  AH,AL
  2413.                CMP   AX,BP             ; ¿es el propio TSR?
  2414.                POP   AX
  2415.                JNE   mx_ul_chain       ; no
  2416.                XPOP  <ES, CX, BX>      ; sí: ¡posible reponer vector!
  2417.                XPUSH <BX, CX, ES>
  2418.                DEC   BX
  2419.                JNZ   mx_ul_norest      ; no es la segunda pasada
  2420.                POP   ES                ; segunda pasada...
  2421.                XPUSH <ES, DS>
  2422.                MOV   BX,CS:mx_ul_tsroff ; restaurar INT's
  2423.                MOV   DS,CS:mx_ul_tsrseg
  2424.                CLI
  2425.                MOV   CX,ES:[SI+1]
  2426.                MOV   [BX+1],CX
  2427.                MOV   CX,ES:[SI+3]
  2428.                MOV   [BX+3],CX
  2429.                STI
  2430.                POP   DS
  2431. mx_ul_norest:  XPOP  <ES, CX>
  2432.                ADD   SI,5              ; siguiente vector
  2433.                DEC   CX
  2434.                JZ    mx_unloadable     ; no más, ¡desinstal-ar/ado!
  2435.                JMP   mx_ul_masvect
  2436. mx_ul_chain:   MOV   CS:mx_ul_tsroff,DI ; ES:DI almacena la dirección
  2437.                MOV   CS:mx_ul_tsrseg,ES ; de la variable vector
  2438.                MOV   DX,ES:[DI+1]
  2439.                MOV   CL,4
  2440.                SHR   DX,CL
  2441.                MOV   CX,ES:[DI+3]
  2442.                ADD   DX,CX             ; INT xx en DX (aprox.)
  2443.                MOV   AH,0BFh
  2444. mx_ul_otro:    INC   AH                ; a por otro TSR
  2445.                JZ    mx_ul_exitnok     ; ¡se acabaron!
  2446.                JMP   mx_ul_masmx
  2447. mx_ul_exitnok: ADD   SP,6              ; equilibrar pila
  2448.                POP   ES
  2449.                STC
  2450.                RET                     ; imposible desinstalar
  2451. mx_unloadable: POP   CX
  2452.                DEC   CX
  2453.                JZ    mx_ul_exitok      ; desinstalado
  2454.                JMP   mx_ul_pasada      ; 1ª pasada exitosa: por la 2ª
  2455. mx_ul_exitok:  TEST  ES:info_extra,111b  ; ¿tipo de instalación?
  2456.                MOV   ES,ES:segmento_real ; segmento real del bloque
  2457.                JZ    mx_ul_freeml        ; cargado en RAM convencional
  2458.                CMP   xms_ins,ON
  2459.                JNE   mx_ul_freeml      ; no hay controlador XMS (¿?)
  2460.                MOV   DX,ES
  2461.                MOV   AH,11h
  2462.                CALL  gestor_XMS        ; liberar memoria superior
  2463.                POP   ES
  2464.                CLC
  2465.                RET
  2466. mx_ul_freeml:  MOV   AH,49h
  2467.                INT   21h               ; liberar bloque de memoria ES:
  2468.                POP   ES
  2469.                CLC
  2470.                RET
  2471. mx_ul_tsrcv?:  XPUSH <AX, ES, DI>       ; ¿es TSR del convenio?...
  2472.                MOV   DI,1492h
  2473.                MOV   ES,DI
  2474.                MOV   DI,1992h
  2475.                INT   2Fh
  2476.                CMP   AX,0FFFFh
  2477.                JNE   mx_ul_ncvexit
  2478.                CMP   WORD PTR ES:[DI-4],"#*"
  2479.                JNE   mx_ul_ncvexit
  2480.                CMP   WORD PTR ES:[DI-2],"*#"
  2481.                JNE   mx_ul_ncvexit
  2482.                ADD   SP,4              ; CF=0
  2483.                POP   AX
  2484.                RET
  2485. mx_ul_ncvexit: XPOP  <DI, ES, AX>      ; ...no es TSR del convenio
  2486.                STC                     ; CF=1
  2487.                RET
  2488. mx_ul_tsroff   DW    0
  2489. mx_ul_tsrseg   DW    0
  2490. mx_unload      ENDP
  2491.  
  2492. ; ------------ Inicializar variable idioma_sp según idioma del país.
  2493.  
  2494. habla_hispana? PROC
  2495.                XPUSH <AX, BX, CX, DX, BP>
  2496.                MOV   AH,30h
  2497.                INT   21h
  2498.                XCHG  AH,AL             ; AX = versión del DOS
  2499.                MOV   BP,AX
  2500.                MOV   idioma_sp,OFF     ; supuesto de habla no hispana
  2501.                CMP   BP,200h
  2502.                JB    habla_ok
  2503.                LEA   DX,buffer_aux
  2504.                MOV   AX,3800h
  2505.                INT   21h               ; obtener información del pais
  2506.                CMP   BP,20Bh
  2507.                JE    habla_ax          ; DOS 2.11: AX cód. telefónico
  2508.                CMP   BP,300h
  2509.                JB    habla_ok          ; 2.x excepto 2.11: mala suerte
  2510.                MOV   AX,BX
  2511. habla_ax:      LEA   BX,paises_sp-2
  2512.                MOV   CX,numpaises_sp
  2513. habla_sp?:     ADD   BX,2
  2514.                CMP   AX,[BX]
  2515.                JE    habla_hispana
  2516.                LOOP  habla_sp?
  2517. habla_ok:      MOV   AL,param_i
  2518.                XOR   idioma_sp,AL      ; considerar parámetro /I
  2519.                XPOP  <BP, DX, CX, BX, AX>
  2520.                RET
  2521. habla_hispana: MOV   idioma_sp,ON      ; país de habla hispana
  2522.                MOV   AL,param_i
  2523.                XOR   idioma_sp,AL      ; considerar parámetro /I
  2524.                XPOP  <BP, DX, CX, BX, AX>
  2525.                RET
  2526. habla_hispana? ENDP
  2527.  
  2528. ; ------------ Imprimir cadena en DS:DX delimitada por un 0 ó un 255.
  2529. ;              Si hay que imprimir en inglés se toma la cadena que va
  2530. ;              después si ésta acaba en 255 (si acaba en 0, no hay
  2531. ;              distinción entre mensaje castellano e inglés).
  2532.  
  2533. print          PROC
  2534.                XPUSH <AX, BX, CX, DX>
  2535. pr_decidir:    CMP   idioma_sp,OFF
  2536.                JE    usar_uk
  2537.                CMP   idioma_sp,ON
  2538.                JE    usar_sp
  2539.                PUSH  DX
  2540.                CALL  habla_hispana?         ; determinar lengua
  2541.                POP   DX
  2542.                JMP   pr_decidir
  2543. usar_uk:       MOV   BX,DX
  2544.                DEC   BX
  2545. usar_uk?:      INC   BX
  2546.                CMP   BYTE PTR [BX],0
  2547.                JE    usar_sp                ; acaba en 0: no traducir
  2548.                CMP   BYTE PTR [BX],255
  2549.                JNE   usar_uk?
  2550.                LEA   DX,[BX+1]              ; acaba en 255: traducir
  2551. usar_sp:       MOV   BX,DX
  2552.                DEC   BX
  2553. print_cad:     INC   BX
  2554.                CMP   BYTE PTR [BX],0
  2555.                JE    prlong_ok
  2556.                CMP   BYTE PTR [BX],255
  2557.                JNE   print_cad              ; calcular longitud
  2558. prlong_ok:     MOV   CX,BX
  2559.                SUB   CX,DX
  2560.                MOV   AH,40h
  2561.                MOV   BX,1
  2562.                INT   21h
  2563.                XPOP  <DX, CX, BX, AX>
  2564.                RET
  2565. print          ENDP
  2566.  
  2567. ; ------------ Informar al usuario.
  2568.  
  2569. info           PROC
  2570.                CMP   param_ayuda,ON    ; ¿solicitud de ayuda?
  2571.                JNE   info_normal
  2572.                LEA   DX,ayuda_txt
  2573.                CALL  print
  2574.                JMP   fin_info
  2575. info_normal:   LEA   DX,programa_txt
  2576.                CALL  print
  2577.                TEST  error,0FFFFh
  2578.                JZ    info_ins          ; no hay error
  2579.                JMP   info_err          ; lo hay: informar del mismo
  2580. info_ins:      LEA   DX,instalado_txt
  2581.                TEST  accion,INSTALADO  ; ¿informar "instalado"?
  2582.                JNZ   acc_ok
  2583.                LEA   DX,ya_instal_txt
  2584.                TEST  accion,YAINSTALADO  ; ¿informar "ya instalado"?
  2585.                JNZ   acc_ok
  2586.                LEA   DX,des_ok_txt     ; informar "desinstalado"
  2587.                CALL  print
  2588.                JMP   fin_info
  2589. acc_ok:        CALL  print             ; instalado/ya instalado...
  2590.                LEA   DX,dma_cross_txt
  2591.                TEST  accion,BUFFERPLUS ; ¿el DMA cruzaba frontera?
  2592.                JZ    dma_ok
  2593.                CALL  print
  2594. dma_ok:        JMP   fin_info
  2595. info_err:      LEA   DX,mal_pc_txt
  2596.                TEST  error,MALPC       ; ¿no es ordenador adecuado?
  2597.                JZ    otroerr1
  2598.                CALL  print
  2599. otroerr1:      LEA   DX,mal_dos_txt
  2600.                TEST  error,MALDOS      ; ¿DOS incorrecto?
  2601.                JZ    otroerr21
  2602.                CALL  print
  2603. otroerr21:     LEA   DX,mal_bios_txt
  2604.                TEST  error,MALBIOS     ; ¿BIOS obsoleta?
  2605.                JZ    otroerr22
  2606.                CALL  print
  2607. otroerr22:     LEA   DX,mal_drv_txt
  2608.                TEST  error,MALDRV      ; ¿Unidades de doble?
  2609.                JZ    otroerr23
  2610.                CALL  print
  2611. otroerr23:     LEA   DX,err_sintax_txt
  2612.                TEST  error,MALSINTAX   ; ¿error de sintaxis?
  2613.                JZ    otroerr3
  2614.                CALL  print
  2615. otroerr3:      LEA   DX,imp_desins_txt
  2616.                TEST  error,NOINSTALADO ; no instalado, ¿piden
  2617.                JZ    otroerr4          ; desinstalar?
  2618.                CALL  print
  2619. otroerr4:      LEA   DX,des_no_ok_txt
  2620.                TEST  error,IMPDESINS   ; ¿imposible desinstalar?
  2621.                JZ    otroerr5
  2622.                CALL  print
  2623. otroerr5:      LEA   DX,inst_sys_txt
  2624.                TEST  error,INSTALADOSYS  ; ¿instalada versión *.SYS?
  2625.                JZ    otroerr5x
  2626.                CALL  print
  2627. otroerr5x:     TEST  error,NOINSTALABLE  ; ¿versión incorrecta?
  2628.                JZ    otroerr6
  2629.                CALL  error_version
  2630. otroerr6:      LEA   DX,nocabe_txt
  2631.                TEST  error,MX64FULL    ; ¿imposible instalar?
  2632.                JZ    otroerr8
  2633.                CALL  print
  2634. otroerr8:      LEA   DX,win_ld_txt
  2635.                TEST  error,WIN_LOAD    ; ¿instalar desde Windows?
  2636.                JZ    otroerr9
  2637.                CALL  print
  2638. otroerr9:      LEA   DX,win_ul_txt
  2639.                TEST  error,WIN_UNLOAD  ; ¿desinstalar desde Windows?
  2640.                JZ    fin_info
  2641.                CALL  print
  2642. fin_info:      RET
  2643. info           ENDP
  2644.  
  2645. ; ------------ Comprobar que la configuración es la adecuada. Para
  2646. ;              saber si la INT 13h de este ordenador acaba llamando a
  2647. ;              la INT 40h, se desvía la INT 40h y se provoca un inocuo
  2648. ;              reset de disquetes vía INT 13h para comprobar si pasa
  2649. ;              por la INT 40h.
  2650.  
  2651. pc_ok?         PROC
  2652.                CALL  test_i40
  2653.                TEST  accion,I40
  2654.                JZ    test_dos          ; no soportada la INT 40h
  2655.                MOV   nueva_i13,40h
  2656.                MOV   vieja_i13,40h     ; usar INT 40 en vez de INT 13
  2657. test_dos:      MOV   AH,30h
  2658.                INT   21h
  2659.                XCHG  AH,AL
  2660.                CMP   AX,31Eh           ; ¿DOS 3.30 o superior?
  2661.                MOV   AX,MALDOS
  2662.                JB    pc_nok
  2663.                CALL  testAT
  2664.                MOV   AX,MALPC
  2665.                JNC   pc_nok            ; [*]
  2666.                TEST  error,MALBIOS
  2667.                JNZ   pc_ok             ; con ese error vale
  2668.                MOV   AX,MALDRV
  2669.                CMP   info_A.tipo_drv,2 ; ¿unidad A: de 1.2?
  2670.                JE    pc_ok
  2671.                CMP   info_A.tipo_drv,4 ; ¿unidad A: de 1.44 ó 2.88?
  2672.                JAE   pc_ok
  2673.                CMP   info_B.tipo_drv,2 ; ¿unidad B: de 1.2?
  2674.                JE    pc_ok
  2675.                CMP   info_B.tipo_drv,4 ; ¿unidad B: de 1.44 ó 2.88?
  2676.                JAE   pc_ok
  2677. pc_nok:        OR    error,AX
  2678. pc_ok:         TEST  error,MALPC
  2679.                JZ    pc_ok?_ret
  2680.                AND   error,MALPC       ; ese error basta
  2681. pc_ok?_ret:    RET
  2682. pc_ok?         ENDP
  2683.  
  2684.                ; --- Comprobar si la INT 40h está en uso
  2685.  
  2686. test_i40:      XPUSH <DS, ES>          ; *
  2687.                MOV   AX,3540h
  2688.                INT   21h
  2689.                XPUSH <ES, BX>          ; vector de INT 40h original
  2690.                LEA   DX,i40_aux
  2691.                MOV   AX,2540h
  2692.                INT   21h               ; establecer nueva INT 40h
  2693.                XOR   AX,AX
  2694.                MOV   DL,0
  2695.                INT   13h               ; reset de disco
  2696.                XPOP  <DX, DS>
  2697.                MOV   AX,2540h
  2698.                INT   21h               ; restaurar INT 40h original
  2699.                XPOP  <ES, DS>          ; *
  2700.                RET
  2701.  
  2702. i40_aux        PROC
  2703.                OR    CS:accion,I40     ; sí utilizada INT 40h
  2704.                IRET                    ; desde la INT 13h
  2705. i40_aux        ENDP
  2706.  
  2707.                ; --- Detectar 286 ó superior.
  2708.  
  2709. testAT         PROC
  2710.                PUSH  AX
  2711.                PUSHF
  2712.                POP   AX
  2713.                OR    AH,70h        ; intentar activar bit 12, 13 ó 14
  2714.                PUSH  AX            ; del registro de estado
  2715.                POPF
  2716.                PUSHF
  2717.                POP   AX
  2718.                AND   AH,0F0h
  2719.                CMP   AH,0F0h
  2720.                JE    testedAT
  2721.                STC
  2722. testedAT:      CMC                 ; CF = 0 en AT y 1 en PC/XT
  2723.                POP   AX
  2724.                RET
  2725. testAT         ENDP
  2726.  
  2727. ; ------------ Comprobar que el buffer para el DMA en la copia
  2728. ;              residente no cruza una frontera. En ese caso se emplea
  2729. ;              otro buffer ubicado tras el habitual, lo que aumenta el
  2730. ;              consumo de memoria de este área. A la entrada AX apunta
  2731. ;              al segmento que contendrá el buffer y BX el offset. Si
  2732. ;              no se produce el cruce, se disminuye «parrafos_resid»
  2733. ;              para economizar memoria; incluso aunque se produzca,
  2734. ;              esta variable se reduce en lo posible. A la vuelta,
  2735. ;              «parrafos_resid» y BX indican la memoria definitiva
  2736. ;              ocupada por el programa.
  2737.  
  2738. testDMA        PROC
  2739.                XPUSH <AX, CX, DX>
  2740.                MOV   CX,16
  2741.                MUL   CX
  2742.                ADD   AX,BX
  2743.                ADC   DX,0              ; DX:AX = dirección 20 bits
  2744.                MOV   CX,DX
  2745.                PUSH  AX
  2746.                ADD   AX,tbuffer-1      ; buffer para el mayor sector
  2747.                ADC   DX,0
  2748.                POP   AX
  2749.                MOV   BX,parrafos_resid
  2750.                SUB   BX,tbuffer/16     ; mejor supuesto posible
  2751.                CMP   DX,CX
  2752.                JE    dmatested
  2753.                NEG   AX
  2754.                ADD   AX,15
  2755.                MOV   CL,4
  2756.                SHR   AX,CL
  2757.                ADD   BX,AX             ; consumo adicional
  2758.                SHL   AX,CL
  2759.                ADD   buffer,AX         ; nueva posición del buffer
  2760.                OR    accion,BUFFERPLUS ; aviso al usuario
  2761. dmatested:     MOV   parrafos_resid,BX
  2762.                XPOP  <DX, CX, AX>
  2763.                RET
  2764. testDMA        ENDP
  2765.  
  2766. ; ------------ Desde Windows, no se permite instalar o desinstalar 2MX
  2767.  
  2768. testWin        PROC
  2769.                PUSH  AX
  2770.                CMP   param_w,ON        ; ¿se indicó parámetro /W?
  2771.                JE    fin_testWin
  2772.                MOV   AX,1600h
  2773.                INT   2Fh
  2774.                AND   AL,AL             ; ¿Windows en modo extendido?
  2775.                JZ    noWinEnh
  2776.                CMP   AL,80h            ; ¿Windows en modo extendido?
  2777.                JE    noWinEnh
  2778. siWin:         STC                     ; estamos dentro de Windows
  2779.                JMP   fin_testWin
  2780. noWinEnh:      MOV   AX,4680h
  2781.                INT   2Fh
  2782.                AND   AX,AX
  2783.                JZ    siWin             ; Windows en modo real/estándar
  2784. fin_testWin:   POP   AX
  2785.                RET                     ; CF=1 si dentro de Windows
  2786. testWin        ENDP
  2787.  
  2788. ; ------------ Calcular la constante de retardo básica para perder
  2789. ;              exactamente 54,925 ms. Con una regla de 3 se podrá
  2790. ;              después aplicar para hacer retardos de milisegundos.
  2791.  
  2792. cte_tiempos    PROC
  2793.                XPUSH <DS, ES, BX, CX, DX>
  2794.                MOV   AX,3508h
  2795.                INT   21h
  2796.                XPUSH <ES, BX>          ; preservar vector de INT 8
  2797.                PUSH  DS
  2798.                MOV   AX,40h
  2799.                MOV   DS,AX
  2800.                MOV   AL,DS:[6Ch]
  2801. espera_i8:     CMP   AL,DS:[6Ch]
  2802.                JE    espera_i8         ; esperar INT 8 ... para que no
  2803.                POP   DS
  2804.                LEA   DX,i8_crono       ; venga otra en un buen rato...
  2805.                MOV   AX,2508h
  2806.                INT   21h               ; nueva rutina de INT 8
  2807.                IN    AL,21h
  2808.                PUSH  AX                ; preservar estado de IRQ's
  2809.                MOV   AL,11111110b
  2810.                OUT   21h,AL            ; permitir sólo IRQ0
  2811.                MOV   AH,0              ; fase
  2812.                MOV   CX,0              ; contador
  2813.                MOV   BX,CX             ; seguiría a 0 si fallara
  2814.                EVEN                    ; forzar alineamiento
  2815. cuenta_iter:   DEC   CX                ; <─┐ bucle básico de retardo
  2816.                JMP   SHORT $+2         ;   │
  2817.                JNZ   cuenta_iter       ; <─┘ lo interrumpirá INT 8
  2818.                POP   AX                ; anterior estado de IRQ's
  2819.                OUT   21h,AL
  2820.                XPOP  <DX, DS>
  2821.                PUSH  BX                ; valor real contado
  2822.                MOV   AX,2508h          ; restaurar vector de INT 8
  2823.                INT   21h
  2824.                POP   AX                ; (65536-AX) vueltas en 54,9 ms
  2825.                NEG   AX                ; constante de retardo básica
  2826.                XPOP  <DX, CX, BX, ES, DS>
  2827.                RET
  2828. i8_crono:      INC   AH                ; nueva INT 8 que interrumpe
  2829.                CMP   AH,1              ; el bucle de retardo
  2830.                JE    fase1
  2831.                CMP   AH,2
  2832.                JE    fase2
  2833. i8_exit:       MOV   AL,20h
  2834.                OUT   20h,AL
  2835.                IRET
  2836. fase1:         MOV   CX,0              ; sincronizar con el reloj
  2837.                JMP   i8_exit
  2838. fase2:         MOV   BX,CX             ; anotar constante de retardo
  2839.                MOV   CX,1              ; forzar fin del bucle
  2840.                JMP   i8_exit
  2841. cte_tiempos    ENDP
  2842.  
  2843. ; ***********************************************
  2844. ; *                                             *
  2845. ; *   D A T O S    N O    R E S I D E N T E S   *
  2846. ; *                                             *
  2847. ; ***********************************************
  2848.  
  2849. ON             EQU   1         ; constantes booleanas
  2850. OFF            EQU   0
  2851.  
  2852. ; ------------ Gestión de memoria y control de instalación.
  2853.  
  2854. xms_ins        DB    OFF       ; a ON si presente controlador XMS
  2855. gestor_XMS     LABEL DWORD     ; dirección del controlador XMS
  2856. XMS_off        DW    0
  2857. XMS_seg        DW    0
  2858.  
  2859. parrafos_resid DW    ?         ; párrafos de memoria consumidos
  2860. alloc_strat    DW    0         ; estrategia asignación (DOS 5)
  2861. umb_state      DB    0         ; estado de bloques UMB (DOS 5)
  2862.  
  2863. tsr_dir        LABEL DWORD     ; dirección de la copia residente
  2864. tsr_off        DW    0
  2865. tsr_seg        DW    0
  2866.  
  2867. mem640         DW    0         ; párrafos de memoria convencional
  2868.  
  2869. offsets_ints   DW    2         ; número de vectores interceptados
  2870. nueva_i13      DB    13h       ; tabla de offsets de los vectores
  2871.                DW    ges_int13 ; de interrupción interceptados
  2872.                DB    2Fh
  2873.                DW    ges_int2F
  2874.  
  2875. param_ml       DB    OFF       ; a ON si se indicó parámetro /ML
  2876. param_u        DB    OFF       ; a ON si se indicó parámetro /U
  2877. param_i        DB    OFF       ; a ON si se indicó parámetro /I
  2878. param_w        DB    OFF       ; a ON si se indicó parámetro /W
  2879. param_ayuda    DB    OFF       ; a ON si se indicó /? /H ó ?
  2880.  
  2881. MALPC          EQU    1        ; Códigos de error
  2882. MALDOS         EQU    2
  2883. MALBIOS        EQU    4
  2884. MALDRV         EQU    8
  2885. MALSINTAX      EQU   16
  2886. NOINSTALADO    EQU   32
  2887. IMPDESINS      EQU   64
  2888. INSTALADOSYS   EQU  128
  2889. NOINSTALABLE   EQU  256
  2890. MX64FULL       EQU  512
  2891. WIN_LOAD       EQU 1024
  2892. WIN_UNLOAD     EQU 2048
  2893.  
  2894. INSTALADO      EQU   1         ; códigos de acción e información
  2895. YAINSTALADO    EQU   2
  2896. DESINSTALADO   EQU   4
  2897. BUFFERPLUS     EQU   8
  2898. I40            EQU  16
  2899.  
  2900. error          DW    0         ; variable para acumular errores
  2901. accion         DB    0         ; variable que indica lo sucedido
  2902.  
  2903. ; ------------ Códigos de modos y órdenes del DMA y del FDC.
  2904.  
  2905. F_READ         EQU   46h               ; modo DMA para lectura
  2906. F_WRITE        EQU   4Ah               ; modo DMA para escritura
  2907. F_VERIFY       EQU   42h               ; modo DMA para verificación
  2908. F_FORMAT       EQU   01001101b         ; orden de formateo del FDC
  2909.  
  2910. ; ------------ Otras variables.
  2911.  
  2912. idioma_sp      DB    5Ah       ; ni en ON ni en OFF al principio
  2913.  
  2914.                ; --- Código telefónico de países de
  2915.                ;     habla hispana (mucha o poca).
  2916.  
  2917. paises_sp      DW    54                ; Argentina
  2918.                DW    591               ; Bolivia
  2919.                DW    57                ; Colombia
  2920.                DW    506               ; Costa Rica
  2921.                DW    56                ; Chile
  2922.                DW    593               ; Ecuador
  2923.                DW    503               ; El Salvador
  2924.                DW    34                ; España
  2925.                DW    63                ; Filipinas
  2926.                DW    502               ; Guatemala
  2927.                DW    504               ; Honduras
  2928.                DW    212               ; Marruecos
  2929.                DW    52                ; México
  2930.                DW    505               ; Nicaragua
  2931.                DW    507               ; Panamá
  2932.                DW    595               ; Paraguay
  2933.                DW    51                ; Perú
  2934.                DW    80                ; Puerto Rico
  2935.                DW    508               ; República Dominicana
  2936.                DW    598               ; Uruguay
  2937.                DW    58                ; Venezuela
  2938.                DW    3                 ; genérico latinoamérica
  2939. numpaises_sp   EQU   ($-OFFSET paises_sp)/2
  2940.  
  2941. ; ------------ Texto.
  2942.  
  2943. programa_txt   DB    13,10,"  2MX 2.1",0
  2944.  
  2945. instalado_txt  DB    " instalado.",13,10,255," installed.",13,10,0
  2946.  
  2947. ya_instal_txt  DB    " ya instalado, base de tiempos ajustada.",13,10,255
  2948.                DB    " already installed, delay constant adjusted.",13,10,0
  2949.  
  2950. nocabe_txt     DB    ": Instalación imposible.",13,10
  2951.                DB    "      Ya hay 64 programas residentes con la "
  2952.                DB    "misma técnica.",13,10,255
  2953.                DB    ": Unable to install.",13,10
  2954.                DB    "      There are already 64 TSR's with the same technique."
  2955.                DB    13,10,0
  2956.  
  2957. err_sintax_txt DB    13,10,"    - Parámetro(s) incorrecto(s)."
  2958.                DB    13,10,"      Ejecute 2MX /? para obtener "
  2959.                DB    "ayuda.",13,10,7,255
  2960.                DB    13,10,"    - Incorrect option(s)."
  2961.                DB    13,10,"      Execute 2MX /? to obtain help."
  2962.                DB    13,10,7,0
  2963.  
  2964. mal_pc_txt     DB    13,10,"    - Error: Necesario ordenador PC/XT. Utilice 2M en esta máquina.",13,10,255
  2965.                DB    13,10,"    - Error: Needs PC/XT system. Use 2M on this computer.",13,10,0
  2966.  
  2967. mal_dos_txt    DB    13,10,"    - Error: Necesaria versión DOS 3.30 ó posterior.",13,10,255
  2968.                DB    13,10,"    - Error: Needs at least DOS 3.30 or above.",13,10,0
  2969.  
  2970. mal_bios_txt   DB    13,10,"    - Error: No puedo detectar el tipo de las unidades. Instale 2M-XBIOS antes.",13,10,255
  2971.                DB    13,10,"    - Error: Impossible to detect drive types. Please install 2M-XBIOS before.",13,10,0
  2972.  
  2973. mal_drv_txt    DB    13,10,"    - Error: Necesaria(s) unidad(es) de alta densidad.",13,10,255
  2974.                DB    13,10,"    - Error: Needs high-density floppy drive(s).",13,10,0
  2975.  
  2976. dma_cross_txt  DB    "    - Nota: El buffer de E/S cruzaba una frontera de DMA y fue ampliado.",13,10
  2977.                DB    "            Cambie la ubicación en memoria de 2MX para ahorrar unos bytes.",13,10,255
  2978.                DB    "    - Note: I/O buffer has been extended because 2MX crosses a DMA boundary.",13,10
  2979.                DB    "            Modify the memory location of 2MX to save a little memory.",13,10,0
  2980.  
  2981. win_ld_txt     DB    " *NO* instalado:",13,10,"    Este programa debe cargarse ANTES de entrar en Windows.",13,10,7,255
  2982.                DB    " *NOT* installed:",13,10,"    This program must be loaded before WINDOWS.",13,10,7,0
  2983. win_ul_txt     DB    " *NO* desinstalado:",13,10,"    Este programa debe descargarse fuera de Windows.",13,10,7,255
  2984.                DB    " *NOT* uninstalled:",13,10,"    This program must be uninstalled outside of WINDOWS.",13,10,7,0
  2985.  
  2986. mal_ver_txt1   DB    13,10,"    - Error: ya está instalada la versión ",255
  2987.                DB    13,10,"    - Error: Version ",0
  2988. mal_ver_txt2   DB    " de este programa.",13,10,7,255
  2989.                DB    " of this program is already installed.",13,10,7,0
  2990.  
  2991. des_ok_txt     DB    " desinstalado.",13,10,255," uninstalled.",13,10,0
  2992.  
  2993. des_no_ok_txt  DB    13,10,"    - Desinstalación imposible (se ha "
  2994.                DB    "instalado después un programa"
  2995.                DB    13,10,"      que no respeta el convenio y tiene "
  2996.                DB    "alguna interrupción común).",13,10,7,255
  2997.                DB    13,10,"    - Uninstall unavailable (it has been"
  2998.                DB    " installed before a program that"
  2999.                DB    13,10,"      not support CiriSOFT convention"
  3000.                DB    " and uses a common interrupt).",13,10,7,0
  3001.  
  3002. imp_desins_txt DB    13,10,"    - Programa aún no instalado: "
  3003.                DB    "imposible desinstalarlo.",13,10,255
  3004.                DB    13,10,"    - Program not installed: "
  3005.                DB    "impossible to uninstall.",13,10,0
  3006.  
  3007. inst_sys_txt   DB    13,10,"    - Instalado como controlador de dispositivo; no desinstalable.",13,10,255
  3008.                DB    13,10,"    - Installed as device driver: unable to uninstall.",13,10,0
  3009.  
  3010. ayuda_txt      LABEL BYTE
  3011. DB 13,10,10
  3012. DB "   2MX 2.1 - CONTROLADOR PARA ACCESO A DISCOS FORMATEADOS CON TECNOLOGIA STV.",13,10
  3013. DB "     (c) 1994 Ciriaco García de Celis - Grupo Universitario de Informática.",13,10
  3014. DB "   C/Renedo, 2, 4-C; 47005 Valladolid (España) - ciri@gui.uva.es - 2:341/21.8",13,10,10
  3015. DB "                           Sintaxis:  2MX [/ML] [/U]",13,10,10
  3016. DB " ■ 2MX es un programa de tipo «CARDWARE» estando autorizada su distribución sin",13,10
  3017. DB " modificaciones y con carácter gratuito. Tras ejecutarlo 2MX se queda residente",13,10
  3018. DB " en memoria para dar soporte a disquetes de formato 2M creados por 2MF.",13,10,10
  3019. DB " ■ 2MX precisa para operar:",13,10
  3020. DB " - Un equipo PC/XT con una BIOS moderna (o 2M-XBIOS instalado en su defecto).",13,10
  3021. DB " - Al menos una disquetera de alta densidad (las de doble no son controladas).",13,10
  3022. DB " - 5296 bytes de memoria superior o en su defecto 5392 de memoria convencional.",13,10
  3023. DB " - Sistema operativo DOS y/o WINDOWS 3.X, otros entornos no son contemplados.",13,10
  3024. DB " - Utilidad 2MF para formatear los disquetes 2M.",13,10
  3025. DB " ■ 2MX ofrece:",13,10
  3026. DB " - Soporte para disquetes de 902K (5¼-DD) hasta 3772K (3½-ED) en A: y en B:.",13,10
  3027. DB " - Soporte INT 13h para que los programas comerciales puedan formatear en 2M.",13,10
  3028. DB " - Soportados comandos DISKCOPY y ¡SYS! hacia discos destino con formato 2M.",13,10,10
  3029. DB " ■ Con /ML se fuerza la instalación en memoria convencional (2MX se autoinstala",13,10
  3030. DB " automáticamente en memoria superior) y con /U se desinstala, si es posible.",13,10
  3031. DB 255
  3032.  
  3033. DB 13,10,10
  3034. DB "    2MX 2.1  -  DISKETTE DRIVE CONTROLLER FOR STV DISKETTE FORMAT TECHNOLOGY.",13,10
  3035. DB "     (c) 1994 Ciriaco García de Celis - Grupo Universitario de Informática.",13,10
  3036. DB "    C/Renedo, 2, 4-C; 47005 Valladolid (Spain) - ciri@gui.uva.es - 2:341/21.8",13,10,10
  3037. DB "                            Syntax:  2MX [/ML] [/U]",13,10,10
  3038. DB " ■ 2MX is a CARDWARE utility;  distribution is only authorized if no charge and",13,10
  3039. DB " with no modify. Before being executed without any option,  the program becomes",13,10
  3040. DB " resident on memory, in order to handle 2M diskettes located in floppy drives.",13,10,10
  3041. DB " ■ 2MX requires:",13,10
  3042. DB " - A PC/XT computer with a modern BIOS (or with 2M-XBIOS installed instead).",13,10
  3043. DB " - At least one high-density floppy drive (double ones are not drived).",13,10
  3044. DB " - 5296 bytes of upper memory or 5392 bytes of conventional memory instead.",13,10
  3045. DB " - DOS and/or WINDOWS 3.X system, another environs are not still supported.",13,10
  3046. DB " - 2MF utility program to format 2M diskettes.",13,10
  3047. DB " ■ 2MX gives you:",13,10
  3048. DB " - Support for diskettes from 902K (5¼-DD) up to 3772K (3½-ED) on A: and B:.",13,10
  3049. DB " - INT 13h code improves a new 2M format service for high-level applications.",13,10
  3050. DB " - Supported DISKCOPY and ... SYS! commands into 2M diskette already formatted.",13,10,10
  3051. DB " ■ With /ML a conventional memory installation can be forced  (this is an upper",13,10
  3052. DB " memory self-installating utility) and /U tells 2MX to uninstall, if possible.",13,10
  3053. DB 0
  3054.  
  3055. buffer_aux     DB    64 DUP (0)   ; buffer para alguna función del DOS
  3056.  
  3057. _PRINCIPAL     ENDS
  3058.                END   inicio
  3059.